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)文章
SpringBoot RedisTemplate分布式鎖的項目實戰(zhàn)
本文主要介紹了SpringBoot RedisTemplate分布式鎖的項目實戰(zhàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05圖解Java經(jīng)典算法插入排序的原理與實現(xiàn)
插入排序的算法描述是一種簡單直觀的排序算法。其原理是通過構(gòu)建有序序列,對于未排序數(shù)據(jù),在已排序序列中從后向前掃描,找到相應(yīng)位置并插入。本文將用Java語言實現(xiàn)插入排序算法并進行可視化,感興趣的可以了解一下2022-09-09java應(yīng)用程序如何自定義log4j配置文件的位置
這篇文章主要介紹了java應(yīng)用程序如何自定義log4j配置文件的位置,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12關(guān)于SpringBoot打包測試、生產(chǎn)環(huán)境方式
這篇文章主要介紹了關(guān)于SpringBoot打包測試、生產(chǎn)環(huán)境方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09@Transaction,@Async在同一個類中注解失效的原因分析及解決
這篇文章主要介紹了@Transaction,@Async在同一個類中注解失效的原因分析及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12詳解Spring Boot Admin監(jiān)控服務(wù)上下線郵件通知
本篇文章主要介紹了詳解Spring Boot Admin監(jiān)控服務(wù)上下線郵件通知,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12BeanUtils.copyProperties復(fù)制對象結(jié)果為空的原因分析
這篇文章主要介紹了BeanUtils.copyProperties復(fù)制對象結(jié)果為空的原因分析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06SpringMVC實現(xiàn)RESTful風(fēng)格:@PathVariable注解的使用方式
這篇文章主要介紹了SpringMVC實現(xiàn)RESTful風(fēng)格:@PathVariable注解的使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11MyBatis中高級多表查詢(ResultMap、association、collection)詳解
文章主要介紹了MyBatis中高級多表查詢的四種方式:ResultMap、association、collection以及自連接查詢,通過定義接口的抽象方法、編寫mapper.xml和測試類,詳細展示了如何根據(jù)復(fù)雜數(shù)據(jù)結(jié)構(gòu)進行數(shù)據(jù)的裝配和查詢,感興趣的朋友一起看看吧2024-11-11