Java中的SPI機制使用解析
SPI機制簡述
SPI(Service Provider Interface的縮寫) 意思是:“服務(wù)提供者的接口”,專門提供給服務(wù)提供者或者擴展框架功能的開發(fā)者去使用的接口。SPI 將服務(wù)接口和服務(wù)實現(xiàn)分離開來,將服務(wù)調(diào)用方和服務(wù)實現(xiàn)方進行解耦,能夠提升程序的擴展性和可維護性,當(dāng)修改或替換服務(wù)實現(xiàn)并不需要修改調(diào)用方。 SPI 是JDK內(nèi)置的一種動態(tài)加載擴展點的實現(xiàn)。
場景
很多框架都使用了JAVA 的 SPI 機制,如:數(shù)據(jù)庫加載驅(qū)動、日志接口以及DUBBO的擴展實現(xiàn)等。
SPI與API的區(qū)別
(1)API(Application Programming Interface的縮寫),一般都是由實現(xiàn)方制定接口并完成對接口的實現(xiàn),調(diào)用方僅依賴接口調(diào)用,并無法選擇不同實現(xiàn)。
(2)SPI(Service Provider Interface的縮寫),一般都是由調(diào)用方來制定接口規(guī)范,將接口規(guī)范提供給外部廠商來實現(xiàn),調(diào)用方在調(diào)用時可以選擇不同的實現(xiàn)。

SPI的實現(xiàn)
(1)定義一個公共接口(一般定義在一個公共的模塊中,實現(xiàn)方和調(diào)用方都會引用該模塊);
(2)實現(xiàn)方需要實現(xiàn)剛剛定義的公共接口;
(3)實現(xiàn)方在“META-INF/services”目錄下新建一個名稱為接口的全限定名的文本文件,文件內(nèi)容為接口實現(xiàn)類的全限定名(一般會在resources目錄下創(chuàng)建如上文件);
(4)調(diào)用方通過ServiceLoader#load方法加載接口的實現(xiàn)類實例。
SPI的示例代碼
在“模塊1”中定義一個公共接口“Hello”,分別在“模塊2”和“模塊2”兩個模塊中實現(xiàn)剛剛定義的接口作為兩個實現(xiàn)方,在“模塊4”中通過ServiceLoader進行測試并作為調(diào)用方。
// 1. 公共接口(模塊1):
package com.hadoopx.common.util;
public interface Hello {
void say();
}
// 2. 具體實現(xiàn)1(模塊2):
package com.hadoopx.m1.test;
import com.hadoopx.common.util.Hello;
public class Chinese implements Hello {
@Override
public void say() {
System.out.println("你好!");
}
}
// 3. 創(chuàng)建文件: resources/META-INF/services/com.hadoopx.common.util.Hello,內(nèi)容如下:
com.hadoopx.m1.test.Chinese
// 4. 具體實現(xiàn)2(模塊3):
package com.hadoopx.m2.test;
import com.hadoopx.common.util.Hello;
public class English implements Hello {
@Override
public void say() {
System.out.println("HELLO WORLD!");
}
}
// 5. 創(chuàng)建文件: resources/META-INF/services/com.hadoopx.common.util.Hello,內(nèi)容如下:
com.hadoopx.m2.test.English
// 6. 調(diào)用方(模塊4):
package com.hadoopx.test;
import com.hadoopx.common.util.Hello;
import java.util.Iterator;
import java.util.ServiceLoader;
public class Test {
public static void main(String[] args) {
ServiceLoader<Hello> loader = ServiceLoader.load(Hello.class);
Iterator<Hello> iterator = loader.iterator();
while (iterator.hasNext()) {
Hello hello = iterator.next();
hello.say();
}
}
}JAVA與SPRING關(guān)于SPI的區(qū)別
SPRING SPI對JAVA SPI進行了封裝和增強,調(diào)用方可以使用SpringFactoriesLoader.loadFactories方法得到配置文件中寫的實現(xiàn)類的集合,其要求如下:
(1)配置文件必須在resources/META-INF/目錄下,文件名必須為spring.factories;
(2)文件內(nèi)容為鍵值對形式,一個鍵可以有多個值(需要用逗號分割),鍵值都要求是類的全限定名,鍵名為接口的全限定名。
在SPRING BOOT的自動裝配過程中,最終會加載META-INF/spring.factories文件,加載的過程是由SpringFactoriesLoader加載的,它會從CLASSPATH下的每個JAR包中搜尋所有META-INF/spring.factories配置文件并解析,然后將其中定義的BEAN注入到SPRING容器。
// 1. 公共接口(模塊1):
package com.hadoopx.common.util;
public interface Hello {
void say();
}
// 2. 具體實現(xiàn)1(模塊2):
package com.hadoopx.m1.test;
import com.hadoopx.common.util.Hello;
public class Chinese implements Hello {
@Override
public void say() {
System.out.println("你好!");
}
}
// 3. 創(chuàng)建/修改文件: resources/META-INF/spring.factories,內(nèi)容如下:
com.hadoopx.common.util.Hello = \
com.hadoopx.m1.test.Chinese
// 4. 具體實現(xiàn)2(模塊3):
package com.hadoopx.m2.test;
import com.hadoopx.common.util.Hello;
public class English implements Hello {
@Override
public void say() {
System.out.println("HELLO WORLD!");
}
}
// 5. 創(chuàng)建/修改文件: resources/META-INF/spring.factories,內(nèi)容如下:
com.hadoopx.common.util.Hello = \
com.hadoopx.m2.test.English
// 6. 調(diào)用方(模塊4):
package com.hadoopx.test;
import com.hadoopx.common.util.Hello;
import java.util.Iterator;
import java.util.ServiceLoader;
public class Test {
public static void main(String[] args) {
List<Hello> hellos = SpringFactoriesLoader.loadFactories(Hello.class, null);
for(Hello hello: hellos) {
hello.say();
};
}
}SPI的缺點
(1)ServiceLoader會遍歷加載所有的實現(xiàn)類,效率相對較低;
(2)當(dāng)由多個ServiceLoader同時加載時,會存在并發(fā)的問題。
到此這篇關(guān)于Java中的SPI機制使用解析的文章就介紹到這了,更多相關(guān)SPI機制使用詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于@JsonSerialize和@JsonInclude注解使用方法
這篇文章主要介紹了@JsonSerialize和@JsonInclude注解使用方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
使用MyBatis 動態(tài)update數(shù)據(jù)
使用mybatis寫sql,需要動態(tài)更新對象數(shù)據(jù),每次需要更新的字段不同,為了防止null空異常,就需要用動態(tài)sql了,下面給大家分享一段代碼關(guān)于mybatis動態(tài)update,需要的朋友參考下2016-11-11
java org.springframework.boot 對redis操作方法
在Spring Boot項目中操作Redis,你可以使用Spring Data Redis,Spring Data Redis是Spring提供的一個用于簡化Redis數(shù)據(jù)訪問的模塊,它提供了一個易于使用的編程模型來與Redis交互,本文給大家介紹java org.springframework.boot 對redis操作方法,感興趣的朋友一起看看吧2025-04-04

