【SPI】淺談JDK中SPI技術


SPI全稱Service Provider Interface,是Java提供的一套用來被第三方實現或者擴展的API,它可以用來啟用框架擴展和替換組件。

常見的 SPI 有 JDBC、日志門面接口、Spring、SpringBoot相關starter組件、Dubbo、JNDI等。

Java SPI 實際上是“基於接口的編程+策略模式+配置文件”組合實現的動態加載機制。

 

 

要使用Java SPI,需要遵循如下約定:
1、當服務提供者提供了接口的一種具體實現后,在jar包的META-INF/services目錄下創建一個以“接口全限定名”為命名的文件,內容為實現類的全限定名;
2、接口實現類所在的jar包放在主程序的classpath中;
3、主程序通過java.util.ServiceLoder動態裝載實現模塊,它通過掃描META-INF/services目錄下的配置文件找到實現類的全限定名,把類加載到JVM;
4、SPI的實現類必須攜帶一個不帶參數的構造方法。

 示例:

先創建四個maven項目,分別為spi-inter(定義標准服務接口)、spi-defaultInterImpl(服務提供方提供的默認實現)、spi-myInterImpl(調用方覺得默認實現不好使,自己寫的實現)、spi-testSpi。

 

在spi-inter 中定義標准服務接口, 然后將項目打包。

1 package com.demo.spi_inter;
2 /**
3  *    服務方定義的標准服務接口
4  */
5 public interface Config {
6     
7     void loadConfig();
8 }

在spi-defaultInterImpl的pom.xml中添加依賴spi-inter,並進行對接口的實現。在src/main/java目錄下新建META-INF/services目錄,並在services中新建文件,文件名為接口的全限定名,

如示例:“com.demo.spi_inter.Config”,內容為接口實現類的全限定名。如示例:”com.demo.spi_defaultInterImpl.DefaultInterImpl“。然后將項目打包。

 1 package com.demo.spi_defaultInterImpl;
 2 
 3 import com.demo.spi_inter.Config;
 4 
 5 public class DefaultInterImpl implements Config{
 6 
 7     @Override
 8     public void loadConfig() {
 9         System.out.println("這是對接口的默認實現");
10     }
11 
12 }

在spi-testSpi的pom.xml文件中添加入spi-defaultInterImpl的依賴。使用 ServiceLoader 來加載配置文件中指定的實現。

 1 package com.demo.spi_testSpi;
 2 
 3 import java.util.Iterator;
 4 import java.util.ServiceLoader;
 5 
 6 import com.demo.spi_inter.Config;
 7 
 8 public class App {
 9     public static void main(String[] args) {
10         System.out.println("開始加載");
11         ServiceLoader<Config> loader = ServiceLoader.load(Config.class);
12         Iterator<Config> iterator = loader.iterator();
13         while (iterator.hasNext()) {
14             Config config = (Config) iterator.next();
15             config.loadConfig();
16         }
17     }
18 }

運行,可得結果:

 

此時,個人調用方覺得默認的實現不太好,於是自己編寫了一個新的實現spi-myInterImpl。並創建了相對應的目錄結構及文件/META-INF/services/com.demo.spi_inter.Config,文件內容為個人實現的全限定名。打包,將依賴引入到spi-testSpi中。

 1 package com.demo.spi_myInterImpl;
 2 
 3 import com.demo.spi_inter.Config;
 4 
 5 public class MyInterImpl implements Config{
 6 
 7     @Override
 8     public void loadConfig() {
 9         System.out.println("這是個人寫的實現");
10     }
11 }

運行spi-testSpi,可以看見控制台輸出:

SPI技術的優劣:

優點
解耦,使得第三方服務模塊的裝配控制的邏輯與調用者的業務代碼分離,而不是耦合在一起。應用程序可以根據實際業務情況啟用框架擴展或替換框架組件。

缺點

雖然ServiceLoader也算是使用的延遲加載,但是基本只能通過遍歷全部獲取,也就是接口的實現類全部加載並實例化一遍。如果你並不想用某些實現類,它也被加載並實例化了,這就造成了浪費。獲取某個實現類的方式不夠靈活,只能通過Iterator形式獲取,不能根據某個參數來獲取對應的實現類。

多個並發多線程使用ServiceLoader類的實例是不安全的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM