java的SPI機制詳解


一、什么是SPI(Service provider interface)

      是JDK內置的一種服務發現機制。例如有個接口,在運行時態給它添加實現,只需要添加一個實現就可以了。

舉個栗子:

java.sql.Driver接口,其他不同廠商可以針對通一個接口做出不同的實現,mysql,orace等數據庫都有不同的實現提供給用戶,而Java的SPI機制可以為某個接口尋找服務實現。

 

 

可以理解為JAVA SPI機制,可以讓你提供一個標准服務接口,其他廠商、服務提供方。可以有不同實現。通過SPI的方式,對項目進行配置(項目代碼的META-INF/services目錄下的配置文件指定實現類的全路徑名),源碼框架即可找到實現類。繼而執行對應的實現方法

 

二、 SPI使用場景

概括地說,適用於:調用者根據實際使用需要,啟用、擴展、或者替換框架的實現策略

比較常見的例子:

  • 數據庫驅動加載接口實現類的加載
    JDBC加載不同類型數據庫的驅動
  • 日志門面接口實現類加載
    SLF4J加載不同提供商的日志實現類
  • Spring
    Spring中大量使用了SPI,比如:對servlet3.0規范對ServletContainerInitializer的實現、自動類型轉換Type Conversion SPI(Converter SPI、Formatter SPI)等
  • Dubbo
    Dubbo中也大量使用SPI的方式實現框架的擴展, 不過它對Java提供的原生SPI做了封裝,允許用戶擴展實現Filter接口

舉個例子:數據庫DriverManager、Spring、ConfiguraleBeanFactory等都用到了SPI機制,這里DriverManager為例,看下如何實現的。

DriverManager是JDC里管理和注冊不通數據庫Driver的工具類。針對一個數據庫、會存在不同的數據庫驅動實現。我們在使用特定驅動實現時,不希望修改現有代碼,想通過簡單的配置,就可以達到效果。在使用mysql驅動的時候,會有一個疑問,DriverManager如何獲得驅動類的?實際上用的是Class.forName(“com.mysql.jdbc.Driver”)加載mysql驅動后,就會 執行其中的靜態代碼,把Driver注冊到DriverManager中。

來我們上源碼:

 

 可以看到內部有個驚呆代碼塊loadInitalDriver方法,這個方法中用到了SPI工具類 ServiceLoader。

上圖截圖可以的看到,先查找jdbc.drivers屬性值,然后通過SPI機制查找驅動

既然是一種機制,就需要遵循某種約定,SPI需要遵循如下約定:

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

 

 

5 總結

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

相比使用提供接口jar包,供第三方服務模塊實現接口的方式,SPI的方式使得源框架,不必關心接口的實現類的路徑,可以不用通過下面的方式獲取接口實現類:

  • 代碼硬編碼import 導入實現類
  • 指定類全路徑反射獲取:例如在JDBC4.0之前,JDBC中獲取數據庫驅動類需要通過Class.forName("com.mysql.jdbc.Driver"),類似語句先動態加載數據庫相關的驅動,然后再進行獲取連接等的操作
  • 第三方服務模塊把接口實現類實例注冊到指定地方,源框架從該處訪問實例

通過SPI的方式,第三方服務模塊實現接口后,在第三方的項目代碼的META-INF/services目錄下的配置文件指定實現類的全路徑名,源碼框架即可找到實現類

缺點

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

 

 

 

DriverManager


免責聲明!

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



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