您好,登錄后才能下訂單哦!
這篇“Java怎么用SPI實現解耦”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Java怎么用SPI實現解耦”文章吧。
SPI的全稱是服務提供接口,可以用其來啟動框架的擴展和替換組件。
其本質是利用 接口實現+策略模式+配置文件來實現對實現類的動態加載。
在具體的使用中,存在一些約定:
(1)規定在 classPath 的 META-INF/services/ 下,創建該接口的全名稱文件
(2)在該文件中,寫入該接口實現類全稱(路徑+文件名),多個實現類的話,分行寫。
(3)用的2時候,使用 java.util.ServiceLoader 的 load(Interface.class),獲取到實現類,就可以使用了。
值得注意的是,接口實現類必須有一個不帶參數的構造方法。
在本應用中,存在兩個模塊,分別為A模塊和B模塊,這兩個模塊中,A模塊是主模塊,B是從模塊,B模塊是依賴A模塊的。但是在目前有一個類,該類中實現在B模塊中,A模塊需要調用這個類的函數,而模塊不能再依賴B模塊,此時需要進行解耦。在本實現中,利用SPI的方式進行解耦實現。具體實現方案為:
(1)在A模塊新建一個接口:MyLogAppender,具體實現為:
/** * @author Huang gen(kenfeng) * @description 自定義的appender接口 * @Since 2021/02/21 **/ public interface MyLogAppender { /** * 獲取實現的appender * @return 返回新建的appender對象 * */ Appender getAppender(); }
這個接口很簡單,只是返回一個appender的對象。對于對象的實際操作,在接口的實現中進行操作。
(2)在B模塊添加對這個接口的實現,具體的操作為:
/** * @author Huang gen(kenfeng) * @description 自定義的appender * @Since 2021/02/21 **/ @Component public class MeshLogAppender extends UnsynchronizedAppenderBase<ILoggingEvent> implements MyLogAppender,ApplicationContextAware { private ApplicationContext applicationContext; public MeshLogAppender(){ } @Override public Appender getAppender() { MeshLogAppender meshLogAppender = new MeshLogAppender(); return meshLogAppender; } @Override protected void append(ILoggingEvent iLoggingEvent) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String std = simpleDateFormat.format(new Date(Long.parseLong(String.valueOf(iLoggingEvent.getTimeStamp())))); String log = std + "\t" + iLoggingEvent.getLevel() +"\t"+"--- ["+ iLoggingEvent.getThreadName()+"]\t"+iLoggingEvent.getCallerData()[0]+":\t "+iLoggingEvent.getMessage(); FlowMessage input = new FlowMessage(); MeshFlowService meshFlowService = SandboxSystemServiceFactory.getService(MeshFlowService.class); Map<String, Object> body = new HashMap<>(2); body.put("log",log); input.setTenantCode(DefaultTenant.get()); input.setAppCode("epoch"); input.setFlowCode("log_broadcast"); input.setBody(body); FlowMessage output = meshFlowService.process(input); if(!StringUtils.isEmpty(output.getErrorMessage())){ throw new RuntimeException("發布日志時,廣播失敗"); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
在該接口的申明和接口的實現中,存在一些小的技巧的實現。在接口中,只聲明一個類的獲取,并沒有實現具體的方法。在實現類中,對這個類進行實例化,new一個新的類并返回,此時用戶根據這個get方法就可以拿到這個實現類,然后進行實現類的一些操作。這樣寫可以帶來兩個好處: i. 代碼更簡潔,接口的代碼簡單易懂 ii. 可以在實現類的構造方法中注入一些參數,當用戶使用時,直接在get方法里面注入即可。
(3) 在實現類所在文件夾下,也就是sandbox-app-epoch-starter中添加一個配置文件,其配置文件的路徑默認為: resources/META-INF/services/,在這個文件夾下新建一個問題,文件名為接口的路徑,內容是實現類的路徑。由此可以實現接口-->實現類的映射。
如上圖中,文件名為:com.alibaba.halo.sandbox.app.util.MyLogAppender
其文件中的內容為:com.alibaba.lattice2.epoch.util.MeshLogAppender
其原理是,當用戶使用接口時,會掃描項目下的所有文件,查找文件名為com.alibaba.halo.sandbox.app.util.MyLogAppender,然后根據其內容來查找到相關的實現類
(4)在A,可以直接使用接口來進行調用,具體實現如下:
ServiceLoader<MyLogAppender> myLoaderInterfaceServiceLoader = ServiceLoader.load(MyLogAppender.class); Iterator<MyLogAppender> myLoaderInterfaceIterator = myLoaderInterfaceServiceLoader.iterator(); while (myLoaderInterfaceIterator.hasNext()){ MyLogAppender myLoaderInterface = myLoaderInterfaceIterator.next(); Appender newAppender = myLoaderInterface.getAppender(); newAppender.setName("application"); newAppender.setContext(loggerContext); newAppender.start(); rootLogger.addAppender(newAppender); }
從上面可以看到,其可以直接調用MyLogAppender接口,利用這個接口獲取的Appender,之后直接賦值即可。
優點:可以實現代碼的解耦
缺點:存在多個實現類的話,無法根據某個參數或者標志位獲取實例,只能通過遍歷獲取,沒有實現所謂的懶加載
以上就是關于“Java怎么用SPI實現解耦”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。