Dubbo 是基於 Java 原生 SPI 機制思想的一個改進,所以,先從 JAVA SPI 機制開始瞭解什麼是 SPI 以後再去學習Dubbo 的 SPI,就比較容易了
什麼是JDK的SPI?
SPI 全稱( service provider interface ),是 JDK 內置的一種服務提供發現機制,目前市面上有很多框架都是用它來做服務的擴展發現,大家耳熟能詳的如 JDBC、日誌框架都有用到;簡單來說,它是一種動態替換髮現的機制。舉個簡單的例子,如果我們定義了一個規範,需要第三方廠商去實現,那麼對於我們應用方來說,只需要集成對應廠商的插件,既可以完成對應規範的實現機制。 形成一種插拔式的擴展手段
SPI 規範總結
實現 SPI,就需要按照 SPI 本身定義的規範來進行配置,SPI規範如下
1. 需要在classpath下創建一個目錄,該目錄命名必須是:META-INF/services
2. 在該目錄下創建一個 properties 文件,該文件需要滿足
以下幾個條件
a) 文件名必須是擴展的接口的全路徑名稱
b) 文件內部描述的是該擴展接口的所有實現類
c) 文件的編碼格式是 UTF-8
3. 通過 java.util.ServiceLoader 的加載機制來發現
真實應用場景
SPI 在 很 多 地 方 有 應 用 , 大 家 可 以 看 看 最 常 用 的java.sql.Driver 驅動。JDK 官方提供了 java.sql.Driver 這個驅動擴展點,但是你們並沒有看到 JDK 中有對應的 Driver實現。 那在哪裏實現呢?以連接 Mysql 爲例,我們需要添加 mysql-connector-java依賴。然後,你們可以在這個jar包中找到SPI的配置信息。如下圖,所以 java.sql.Driver 由各個數據庫廠商自行實現。這就是 SPI 的實際應用。當然除了這個意外,大家在 spring的包中也可以看到相應的痕跡
按照同樣思路可實現啊:
比如說我們連接數據,DataSource定義了規範,然後Mysql和oracle自己實現規範,最後我們要選哪一最後作爲我們的規範,完全是由咱們自己來決定。
如下圖所示:
JDK SPI缺點:
1. JDK標準的SPI會一次性加載實例化擴展點的所有實現,什麼意思呢?就是如果你在 META-INF/service 下的文件裏面加了 N 個實現類,那麼 JDK 啓動的時候都會一次性全部加載。那麼如果有的擴展點實現初始化很耗時或者如果做技術人的之路明燈,做職場生涯的精神導師有些實現類並沒有用到,那麼會很浪費資源
2. 如果擴展點加載失敗,會導致調用方報錯,而且這個錯誤很難定位到是這個原因
新建項目spi-demo,同時創建module:api,mysql,oracle,demo
api
package com.tian; public interface DataSource { String connect(String msg); }
mysql
package com.tian.mysql; import com.tian.DataSource; public class MysqlDataSource implements DataSource { @Override public String connect(String msg) { System.out.println("mysql"); return null; } }
oracle
package com.tian.oracle; import com.tian.DataSource; public class OraclelDataSource implements DataSource { @Override public String connect(String msg) { System.out.println("oracle"); return null; } }
demo
這裏是把兩種都加載進來了,
package com.tian; import java.util.ServiceLoader; public class DataSourceFactory { public DataSourceFactory() { } public static void getDataSource() { ServiceLoader<DataSource> serviceLoader = ServiceLoader.load(DataSource.class); for (DataSource dataSource : serviceLoader) { dataSource.connect("1111"); } } }
package com.tian; public class DemoMain { public static void main(String[] args) { DataSourceFactory.getDataSource(); } }
運行Demo輸出
如果我們只在demo中pom.xml中導入mysql.jar。
再次運行Demo。那麼最後輸出:
自此咱們已經實現了JDK的SPI。後面Dubbo源碼裏面大量使用SPI機制(Dubbo中是JDK-SPI的升級版本)