這篇文章由三個問題貫穿分別是
一、啥是SPI?
二、啥是策略模式?
三、SPI和策略模式有啥關係?
一、啥是SPI?
入職初期經常聽大佬說SPI,也不知道是幹啥的,最近終於有所領悟。
Java程式設計師應該對『面向介面』程式設計不陌生(如果陌生說明你對物件導向理解的還不夠透徹),我們要說的SPI和麵向介面程式設計是緊密關聯的。
簡單地講,如果一個介面由呼叫方
來定義,而介面的實現由提供方
來實現,這個就是SPI。而如果介面的定義和實現都由提供方
來完成,就是我們常說的API。
這樣說可能還是比較抽象,不如來看一個例子。
src.zip/rt-jar內定義了一個介面Driver,在這個例子中src包扮演的角色是呼叫方
public interface Driver {
Connection connect(String url, java.util.Properties info)
throws SQLException;
...省略
}
複製程式碼
既然src定義了介面,那麼他肯定要用啊,具體細節忽略我們只關心下面這行程式碼
public class DriverManager {
...
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
....
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
}
複製程式碼
小結:
- 呼叫方提供了介面Driver
- 呼叫方面向Driver介面進行了程式設計
下面看看提供方做了啥
首先介面提供方mysql-connector-java登場,我們重點關注兩個地方
1)介面的具體實現
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
複製程式碼
2)名為java.sql.Driver的配置檔案
com.mysql.jdbc.Driver
複製程式碼
小結:
- 實現了src中定義的介面
- 定義了一個不知道幹啥的配置檔案
我們再回到方法呼叫方src中(注意src是需要引入mysql-connector-java這個包的),有這樣一行程式碼
ServiceLoader.load(Driver.class);
ServiceLoader讀取mysql-connector-java包中的那個配置檔案中定義的實現類的名字,通過反射獲取對應類例項~
這樣我們的介面就被指定具體的實現了~接下來我們來看策略模式
二、啥是策略模式?
在閻巨集博士的《JAVA與模式》一書中開頭是這樣描述策略(Strategy)模式的:
策略模式屬於物件的行為模式。其用意是針對一組演算法,將每一個演算法封裝到具有共同介面的獨立的類中,從而使得它們可以相互替換。策略模式使得演算法可以在不影響到客戶端的情況下發生變化。
我們還是通過一個例子來看
interface Calculator {
int doExecute(int a, int b);
}
class AddCalculator implements Calculator{
public int doExecute(int a, int b){
return a+b;
}
}
class MinusCalculator implements Calculator{
public int doExecute(int a, int b){
return a-b;
}
}
public class Test {
Calculator cal;
public void setCal(Calculator cal){
this.cal = cal;
}
public void doCalculate(int a, int b){
cal.doExecute(a, b);
}
public static void main(String[] args) {
Test t = new Test();
t.setCal(new AddCalculator());
t.doCalculator(1,2);
t.setCal(new MinusCalculator());
t.doCalculator(2,1);
}
}
複製程式碼
在Test類的doCalculate方法中並不關心介面的具體實現,這就是一『面向介面』程式設計的例子,我們看到介面的具體實現在main方法也就是介面的呼叫處來指定實現,這個思想和IOC的思想又是類似的~
那麼看我們的最後一個問題,SPI和策略模式有啥關係
三、SPI和策略模式有啥關係?
聰明的小夥伴應該已經看出端倪
SPI模式和策略模式都是面向介面程式設計的典範,而且介面的具體實現由呼叫方來指定。策略模式的例子如上,對於第一個SPI例子來說,假如我們在開發一個java專案,使用的資料庫是oracle,那麼我們引入oracle的jar包即可,顯然該jar包中一定包含一個Driver的實現類以及執行實現類的配置檔案,這個時候我們的java程式就可以『無痕』地使用oracle提供的Driver類了
這裡再多說一句,所有的java專案都會依賴src包的~