SPI與API

蘇格團隊發表於2018-10-27
  • 蘇格團隊
  • 作者:Handsome

SPI與API

SPI與我們熟知的API名字上有點相似,SPI被稱為服務提供介面,API稱為應用程式介面,兩者的區別大致可以這樣來對比。 假設有客戶方和服務方,彼此通過約定的介面對接。

  1. 服務方暴露自己的業務供客戶方呼叫,則為提供API服務。
  2. 客戶方實現服務方提供的介面,然後讓服務方去呼叫自己,則為提供SPI服務。

SPI與API

我們平時最常見的SPI服務就是JDBC。通過統一的JDBC規範,客戶方可以自己實現各種資料來源。試想一下,假設開發者想將資料來源從Mysql切換到Oracle,如果沒有使用JDBC,切換的過程就需要耗費巨大的人力成本。

如何解決上述的資料來源切換問題,這裡需要說一下依賴倒置原則。

  1. 高層模組不應該依賴低層模組,兩者都應該依賴抽象
  2. 抽象不應該依賴細節
  3. 細節應該依賴抽象

JDBC連線Mysql

Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(
              "jdbc:mysql://localhost:3306/test", "root", "123456");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from Users");
複製程式碼

下面自己實現一個簡易版的JDBC程式。 驅動管理程式,負責載入各SPI服務,用一個Map儲存所有載入過的驅動程式,key為SPI服務自定義的名稱。在呼叫connection時,只要url中的字首為驅動名,則使用該驅動建立連線。

/**
 * 驅動管理,負責載入各客戶方提供的資料來源服務/
 */
public class MyDriverManager {
   /**
     * 所有註冊的資料來源連線服務/
    */
private static final Map<String, MyDriver> registerDriver = new HashMap<String, MyDriver>();

    public static void registerDriver(String name, MyDriver driver) {
        registerDriver.put(name, driver);
    }

    public static MyConnection getConnection(String url) {
        for (String key : registerDriver.keySet()) {
            if (url.startsWith(key)) {
                return registerDriver.get(key).getConnection(url);
            }
        }
        throw new RuntimeException("no such provider");
    }
}

複製程式碼

定義驅動介面

public interface MyDriver {
		/**
		 * 獲取連線
		 */
		MyConnection getConnection(String url);
}
複製程式碼

客戶方自己實現的驅動程式,靜態程式碼塊中執行註冊服務,將驅動註冊到驅動管理程式中。

public class MysqlDriver implements MyDriver {

    static {
        MyDriverManager.registerDriver(“mysql”, new MysqlDriver());
    }

    public MyConnection getConnection(String url) {
        System.out.println(“connect to mysql: url = “ + url);
        return new MysqlConnection();
    }
}
複製程式碼

實際執行時,要先使用Class.forName載入一下驅動程式,否則static程式碼塊不會執行,無法將驅動載入到驅動管理程式中。

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {
        		 Class.forName(“com.github.yaolang.chapter01.spi1.MysqlDriver”);
        MyConnection myConnection = MyDriverManager.getConnection(“mysql://localhost:8080”);
        System.out.println(myConnection);
    }
}
複製程式碼

以下為輸出結果

connect to mysql: url = mysql://localhost:8080
com.github.yaolang.chapter01.spi1.MysqlConnection@60e53b93
複製程式碼

相關文章