SPI加載機制
SPI(Service Provider Interface)是一種通過外界配置來加載具體代碼內容的技術手段。SPI是JDK內置的一種服務提供發現機制,用于實現框架的擴展和組件替換。
在SPI中,框架提供一整套接口,使用者實現這些接口后,在classpath的目錄META-INF/services/
下創建以該接口命名的文件,并在該接口中寫下實現類的全包名,SPI的加載機制則會加載該文件中類名對應的類。
下面是一個簡單樣例
首先定義一個接口
package cn.bobasyu.spi;public interface ISpiTest {void test();
}
然后是這個接口的實現類
package cn.bobasyu.spi.impl;import cn.bobasyu.spi.ISpiTest;public class ASpiTestImpl implements ISpiTest {@Overridepublic void test() {System.out.println("Hello A!");}
}
package cn.bobasyu.spi.impl;import cn.bobasyu.spi.ISpiTest;public class BSpiTestImpl implements ISpiTest {@Overridepublic void test() {System.out.println("Hello B!");}
}
接著如前面所述的,在lasspath的目錄META-INF/services/
下創建以該接口命名的文件,并在該文件中寫入兩個實現類的名稱
文件中的內容:
cn.bobasyu.spi.impl.ASpiTestImpl
cn.bobasyu.spi.impl.BSpiTestImpl
在運行時,使用ADK自帶的ServiceLoader進行加載,即可讀取文件中的類名并加載好對應的對象
@Testpublic void spiLoadTest() {ServiceLoader<ISpiTest> serviceLoader = ServiceLoader.load(ISpiTest.class);for (ISpiTest spiTest : serviceLoader) {spiTest.test();}}
SPI在許多地方都有使用,比如在JDBC中,定義了java.sql.Driver
接口,接下來針對每個數據庫的具體實現需要在META-INF/services/
中放入相應的文件,下面是mysql中的例子
文件中的內容:
使用SPI可以實現框架設計者和具體使用者間的解耦,在進行系統架構設計時,只關注抽象的部分,而框架的使用者則可以根據自己的需求進行自定義的擴展,其體現的是數據模式中的橋接模式,抽象部分與實現部分分離。
角色名 | 含義 |
---|---|
抽象化(Abstraction)角色 | 抽象化給出的定義,并保存一個對實現化對象的引用 |
修正抽象化(RefinedAbstraction)角色 | 擴展抽象化角色,改變和修正父類對抽象化的定義 |
實現化(Implementor)角色 | 這個角色給出實現化角色的接口,但不給出具體的實現。必須指出的是,這個接口不一定和抽象化角色的接口定義相同,實際上,這兩個接口可以非常不一樣。實現化角色應當只給出底層操作,而抽象化角色應當只給出基于底層操作的更高一層的操作 |
具體實現化(ConcreteImplementor)角色 | 這個角色給出實現化角色接口的具體實現 |