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