原文:https://www.cnkirito.moe/spi/
一、實現一個自定義的SPI
1.項目結構
- invoker是用來測試的主項目
- interface是針對廠商和插件商定義的接口項目,只提供接口,不提供實現
- good-printer,bad-printer分別是兩個廠商對interface的不同實現,所以他們會依賴於interface項目
主要實現的是,在不改變invoker代碼,只更改依賴的前提下,切換interface的實現廠商
2.interface模塊
com.spi.Printer
public interface Printer {
void print();
}
interface只定義一個接口,不提供實現。規範的制定方一般都是比較牛叉的存在,這些接口通常位於java,javax前綴的包中,這裏的printer只是模擬的一個規範接口。
3.good-printer模塊
3.1 good-printer\pom.xml
<dependencies>
<dependency>
<groupId>com.lara</groupId>
<artifactId>interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
規範的具體實現類必然要依賴規範接口
3.2 com.spi.GoodPrinter
public class GoodPrinter implements Printer {
public void print() {
System.out.println("you are good");
}
}
作爲printer規範接口的實現一
3.3 在resource下添加services文件
每一個SPI接口都需要在自己項目的靜態資源目錄中聲明一個services文件,文件名爲實現規範接口的類名全路徑,此例中便是 com.spi.Printer,文件中的內同則爲實現類的全路徑,此例中便是com.spi.GoodPrinter.
這樣一個廠商的實現便完成了。
4.bad-printer模塊
和上述的good-printer的實現一樣
4.1 bad-printer\pom.xml
<dependencies>
<dependency>
<groupId>com.lara</groupId>
<artifactId>interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
4.2com.spi.BadPrinter
public class BadPrinter implements Printer {
public void print() {
System.out.println("you are bad");
}
}
4.3在resource下添加services文件
5.invoker模塊
這裏的invoker便是我們自己的項目了。如果一開始我們想使用廠商bad-printer的Printer實現,是需要將其的依賴引入
<dependencies>
<dependency>
<groupId>com.lara</groupId>
<artifactId>interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.lara</groupId>
<artifactId>bad-printer</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
5.1編寫調用主類
public class MainClass {
public static void main(String[] args) {
ServiceLoader<Printer> printLoaders = ServiceLoader.load(Printer.class);
for (Printer printer : printLoaders){
printer.print();
}
}
}
ServiceLoader是java.util提個的用於加載固定類路徑下文件的一個加載器,正式它加載了對應接口聲明的實現類。
5.2打印結果1
在後續的方案中,想替換廠商的Printer實現,只需要將依賴更換,調用主類無需變更代碼,符合開閉原則。