JAVA SPI機制詳解使用方法
寫在前面
Java SPI提供了一種為某個接口尋找服務(wù)實現(xiàn)的機制。有點類似IOC的思想,就是將裝配的控制權(quán)移到程序之外,在模塊化設(shè)計中這個機制尤其重要,SPI的核心思想就是解耦。
什么是SPI
SPI全稱Service Provider Interface,是Java提供的一套用來被第三方實現(xiàn)或者擴展的API,它可以用來啟用框架擴展和替換組件。Java SPI 實際上是“基于接口的編程+策略模式+配置文件”組合實現(xiàn)的動態(tài)加載機制。在面向?qū)ο蟮木幊膛c設(shè)計中,一般推薦模塊之間要基于接口編程,模塊之間不對實現(xiàn)類進行硬編碼,一旦代碼里涉及了具體的實現(xiàn)類,就違反了可拔插的原則,如果需要替換另外一種實現(xiàn),就需要修改代碼。
使用場景
使用Java SPI機制的優(yōu)勢是實現(xiàn)解耦,使得第三方服務(wù)模塊的裝配控制的邏輯與調(diào)用者的業(yè)務(wù)代碼分離,而不是耦合在一起,應(yīng)用程序可以根據(jù)實際業(yè)務(wù)情況啟用框架擴展或替換框架組件,常見的場景如下:
(1). JDBC加載不同類型的驅(qū)動
(2). SLF4J對Log4j/Logback的支持
…
實現(xiàn)約定
(1). 服務(wù)提供者提供接口的具體實現(xiàn),在JAVA包的META-INF/services目錄下創(chuàng)建一個以“接口全限定名”為命名的文件,內(nèi)容為實現(xiàn)類的全限定名;
(2). 接口具體實現(xiàn)類所在的JAR包需要放在主程序的CLASSPATH中;
(3). 主程序通過java.util.ServiceLoder動態(tài)加載具體的實現(xiàn)模塊,它通過掃描META-INF/services目錄下的配置文件,找到具體實現(xiàn)類并把它加載到JVM中;
(4). SPI的實現(xiàn)類必須攜帶一個不帶參數(shù)的構(gòu)造函數(shù)。
四種角色
(1). 提供某種功能的接口(SLF4J 提供了一組接口類)
(2). 提供某種功能接口的具體實現(xiàn)(每個具體的實現(xiàn)需要包含:META-INF/services目錄下創(chuàng)建一個以“接口全限定名”為命名的文件,內(nèi)容為實現(xiàn)類的全限定名。Log4j/Logback提供了具體的實現(xiàn))
(3). 提供發(fā)現(xiàn)和加載CLASSPATH中所有的接口具體實現(xiàn)的對象
(4). 客戶端(接口的使用者)
關(guān)于JAVA SPI詳細的介紹請看:JAVA - SPI機制使用詳解(一)
基于JAVA原生特性實現(xiàn)的JAVA SPI機制的DEMO
1. 主要角色
主要角色有:接口、多個實現(xiàn)類以及測試客戶端,在每個實現(xiàn)類中需要創(chuàng)建信息文件:resources/META-INF/services/接口全限定名一致的文件。接口、多個實現(xiàn)類以及客戶端分別在不同的MODULE中。
2. 示例代碼
①. 接口:
package com.hadoopx.javax.spi; public interface Coder { public String write(); }
②. 第一個實現(xiàn)類:
package com.hadoopx.javax.spi; public class Javaer implements Coder { public String write() { return "I'M JAVA CODER, USE JAVA TO WRITE EVERYTHING."; } } 創(chuàng)建信息說明文件:resources/META-INF/services/com.hadoopx.javax.spi.Coder, 里面的內(nèi)容為:com.hadoopx.javax.spi.Javaer
③. 第二個實現(xiàn)類:
package com.hadoopx.javax.spi; public class Rubyer implements Coder { public String write() { return "I'M RUBY CODER, USE RUBY TO WRITE EVERYTHING."; } } 創(chuàng)建信息說明文件:resources/META-INF/services/com.hadoopx.javax.spi.Coder, 里面的內(nèi)容為:com.hadoopx.javax.spi.Rubyer
④. 客戶端:
添加依賴: <dependencies> <dependency> <groupId>com.hadoopx</groupId> <artifactId>javax-spi001-javaer</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.hadoopx</groupId> <artifactId>javax-spi001-rubyer</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
客戶端代碼: package com.hadoopx.javax.spi; public class MyTest { public static void main(String[] args) { ServiceLoader<Coder> s = ServiceLoader.load(Coder.class); Iterator<Coder> iterator = s.iterator(); while (iterator.hasNext()) { Coder lang = iterator.next(); String content = lang.write(); System.out.println(content); } } }
輸出結(jié)果為:
I'M JAVA CODER, USE JAVA TO WRITE EVERYTHING.
I'M RUBY CODER, USE RUBY TO WRITE EVERYTHING.
3. 說明
在實際的使用過程中,需要指定不同的類型來創(chuàng)建不同的實現(xiàn)類實例。
基于SPRING BOOT實現(xiàn)的JAVA SPI機制的DEMO
注意: 在每個實現(xiàn)類中不需要創(chuàng)建信息文件。
①. 接口:
package com.hadoopx.javax.spi; public interface Coder { public String write(); }
②. 第一個實現(xiàn)類:
package com.hadoopx.javax.spi; @Service @Primary public class Javaer implements Coder { public String write() { return "I'M JAVA CODER, USE JAVA TO WRITE EVERYTHING."; } }
③. 第二個實現(xiàn)類:
package com.hadoopx.javax.spi; @Service public class Rubyer implements Coder { public String write() { return "I'M RUBY CODER, USE RUBY TO WRITE EVERYTHING."; } }
④. 客戶端一:
添加依賴: <dependencies> <dependency> <groupId>com.hadoopx</groupId> <artifactId>javax-spi002-javaer</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.hadoopx</groupId> <artifactId>javax-spi002-rubyer</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
客戶端代碼: @RestController @RequestMapping("/t") @Api(value = "測試服務(wù)", description = "該服務(wù)僅僅用于完成驗證、學(xué)習(xí)和測試") public class TestController { // 切換不同的服務(wù) @Autowired @Qualifier("javaer") private Coder coder; @ApiOperation(value = "測試", notes = "基于SPRING BOOT實現(xiàn)的JAVA SPI機制的DEMO") @GetMapping("/spi") public String test() { System.out.println(coder.write()); return "ok"; } }
輸出結(jié)果為:
I'M JAVA CODER, USE JAVA TO WRITE EVERYTHING.
⑤. 客戶端二:
有時會根據(jù)不同的情況,調(diào)用不同服務(wù)的方法,所以在客戶端中需要多增加下面這個文件:
@Service public class CoderContext { // 通過 @Autowired 把Coder所有的實現(xiàn)類注入到map(coders)中. // Spring會查找應(yīng)用的上下文里類型為Coder的Bean, 并把查找到的Bean注入到Map<String, Coder> 或者 List<Coder>中 @Autowired Map<String, Coder> coders; public Coder getCoder(String key){ return coders.get(key); } }
客戶端代碼: @RestController @RequestMapping("/t") @Api(value = "測試服務(wù)", description = "該服務(wù)僅僅用于完成驗證、學(xué)習(xí)和測試") public class TestController { @Autowired private CoderContext coderContext; @ApiOperation(value = "測試", notes = "基于SPRING BOOT實現(xiàn)的JAVA SPI機制的DEMO") @GetMapping("/spi") public String test(String type) { System.out.println(coderContext.getCoder(type).write()); return "ok"; } }
到此這篇關(guān)于JAVA SPI機制詳解使用方法的文章就介紹到這了,更多相關(guān)JAVA SPI機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決spring-boot2.0.6中webflux無法獲得請求IP的問題
這幾天在用 spring-boot 2 的 webflux 重構(gòu)一個工程,寫到了一個需要獲得客戶端請求 IP 的地方,在寫的過程中遇到很多問題,下面小編通過一段代碼給大家介紹解決spring-boot2.0.6中webflux無法獲得請求IP的問題,感興趣的朋友跟隨小編一起看看吧2018-10-10java正則匹配HTML中a標(biāo)簽里的中文字符示例
這篇文章主要介紹了java正則匹配HTML中a標(biāo)簽里的中文字符,涉及java中文正則及HTML元素操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下2017-01-01SpringBoot入門實現(xiàn)第一個SpringBoot項目
今天我們一起來完成一個簡單的SpringBoot(Hello World)。就把他作為你的第一個SpringBoot項目。具有一定的參考價值,感興趣的可以了解一下2021-09-09Object類toString()和equals()方法使用解析
這篇文章主要介紹了Object類toString()和equals()方法使用解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-02-02SpringCloud Alibaba Seata (收藏版)
Seata是一款開源的分布式事務(wù)解決方案,致力于在微服務(wù)架構(gòu)在提供高性能和簡單一樣的分布式事務(wù)服務(wù)。這篇文章主要介紹了SpringCloud Alibaba Seata 的相關(guān)知識,需要的朋友可以參考下2020-10-10Java之MyBatis的Dao方式以及Dao動態(tài)代理詳解
這篇文章主要介紹了Java之MyBatis的Dao方式以及Dao動態(tài)代理詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12java實現(xiàn)從網(wǎng)上下載圖片到本地的方法
這篇文章主要介紹了java實現(xiàn)從網(wǎng)上下載圖片到本地的方法,涉及java針對文件操作的相關(guān)技巧,非常簡單實用,需要的朋友可以參考下2015-07-07