SPI與API

SPI與我們熟知的API名字上有點(diǎn)相似,SPI被稱為服務(wù)提供接口,API稱為應(yīng)用程序接口,兩者的區(qū)別大致可以這樣來對(duì)比。
假設(shè)有客戶方和服務(wù)方,彼此通過約定的接口對(duì)接。
1. 服務(wù)方暴露自己的業(yè)務(wù)供客戶方調(diào)用,則為提供API服務(wù)。
2. 客戶方實(shí)現(xiàn)服務(wù)方提供的接口,然后讓服務(wù)方去調(diào)用自己,則為提供SPI服務(wù)。

SPI與API的區(qū)別

我們平時(shí)最常見的SPI服務(wù)就是JDBC。通過統(tǒng)一的JDBC規(guī)范,客戶方可以自己實(shí)現(xiàn)各種數(shù)據(jù)源。試想一下,假設(shè)開發(fā)者想將數(shù)據(jù)源從Mysql切換到Oracle,如果沒有使用JDBC,切換的過程就需要耗費(fèi)巨大的人力成本。
如何解決上述的數(shù)據(jù)源切換問題,這里需要說一下依賴倒置原則。

  1. 高層模塊不應(yīng)該依賴低層模塊,兩者都應(yīng)該依賴抽象
  2. 抽象不應(yīng)該依賴細(xì)節(jié)
  3. 細(xì)節(jié)應(yīng)該依賴抽象

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");

下面自己實(shí)現(xiàn)一個(gè)簡易版的JDBC程序。
驅(qū)動(dòng)管理程序,負(fù)責(zé)加載各SPI服務(wù),用一個(gè)Map保存所有加載過的驅(qū)動(dòng)程序,key為SPI服務(wù)自定義的名稱。在調(diào)用connection時(shí),只要url中的前綴為驅(qū)動(dòng)名,則使用該驅(qū)動(dòng)創(chuàng)建連接。

/**
 * 驅(qū)動(dòng)管理,負(fù)責(zé)加載各客戶方提供的數(shù)據(jù)源服務(wù)/
 */
public class MyDriverManager {
   /**
     * 所有注冊(cè)的數(shù)據(jù)源連接服務(wù)/
    */
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");
    }
}

定義驅(qū)動(dòng)接口

public interface MyDriver {
        /**
         * 獲取連接
         */
        MyConnection getConnection(String url);
}

客戶方自己實(shí)現(xiàn)的驅(qū)動(dòng)程序,靜態(tài)代碼塊中執(zhí)行注冊(cè)服務(wù),將驅(qū)動(dòng)注冊(cè)到驅(qū)動(dòng)管理程序中。

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();
    }
}

實(shí)際執(zhí)行時(shí),要先使用Class.forName加載一下驅(qū)動(dòng)程序,否則static代碼塊不會(huì)執(zhí)行,無法將驅(qū)動(dòng)加載到驅(qū)動(dòng)管理程序中。

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);
    }
}

以下為輸出結(jié)果

connect to mysql: url = mysql://localhost:8080
com.github.yaolang.chapter01.spi1.MysqlConnection@60e53b93

下一篇:ServiceLoader詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容