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ù)僅僅用于完成驗證、學習和測試")
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ù)僅僅用于完成驗證、學習和測試")
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)文章
java使用xfire搭建webservice服務(wù)的過程詳解
這篇文章主要介紹了java使用xfire搭建webservice服務(wù)的過程。使用xfire搭建webService的服務(wù),可以在瀏覽器訪問。對此感興趣的可以了解一下2020-07-07
SpringBoot2.0.3打印默認數(shù)據(jù)源為 HikariDataSource (null)問題
這篇文章主要介紹了SpringBoot2.0.3打印默認數(shù)據(jù)源為 HikariDataSource (null)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
java servlet結(jié)合mysql搭建java web開發(fā)環(huán)境
之前寫過一篇 servlet+oracle的文章,但是那是因為公司有可能接那么一個項目,然后我當時也比較閑,所以隨便學了下,那玩意是白去研究了,因為公司后面并沒接到那項目。2015-12-12

