Java spi機制


01. Java SPI機制

最近在一個日志標准化的項目中,使用了責任鏈模式來鏈接每一個具體的處理Handler.但是在實例化時,需要每一個都去創建實例。
如:

/**
 * 初始化具體的處理類
 */
private void initConcreteHandler() {

    handlers.add(new BasicParamHandler());
    handlers.add(new CommonParamHandler());
    handlers.add(new TestParamHandler());
 	……
}

這種,涉及了具體的實現類,就違反了可拔插的原則,如果需要替換一種實現,就需要修改代碼。老大看到我的代碼,直接讓用Java spi機制去做。

1. SPI機制簡介

Service Provider Interface:服務提供者接口.例如,系統里抽象的各個模塊,往往有很多不同的實現方案,比如日志模塊的方案,xml解析模塊、jdbc模塊的方案等。

面向的對象的設計里,我們一般推薦模塊之間基於接口編程,模塊之間不對實現類進行硬編碼。為了實現在模塊裝配的時候能不在程序里動態指明,這就需要一種服務發現機制。在模塊化設計中這個機制尤其重要。

java spi就是提供這樣的一個機制:為某個接口尋找服務實現的機制。

2. 實現案例

1.common-logging

apache最早提供的日志的門面接口。只有接口,沒有實現。具體方案由各提供商實現, 發現日志提供商是通過掃描 META-INF/services/org.apache.commons.logging.LogFactory配置文件,通過讀取該文件的內容找到日志提工商實現類。只要我們的日志實現里包含了這個文件,並在文件里制定 LogFactory工廠接口的實現類即可。

2. JDBC

jdbc4.0以前, 開發人員還需要基於Class.forName("xxx")的方式來裝載驅動。
創建連接:

DriverManage.getConnection()中,有Connection con = aDriver.driver.connect(url, info);

driver成員變量,是java.sql.Driver接口,Java對外公開的一個加載驅動接口,Java並未實現,至於實現這個接口由各個Jdbc廠商去實現。

如MySQL,mysql-connector-java-5.1.38.jar包下面META-INF.services包下有個java.sql.Driver文件打開文件有下面兩行
com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver

3. 具體實現

(1)如,我們這里的處理模塊。面向接口編程。這是我的一種實現方式。

public interface LogProcessHandler {

/**
 * 解析日志,具體的給Bean賦值的邏輯
 * @param context 上下文
 */
void process(ProcessorContext context);

/**
 * 日志解析handler拓撲順序,從小到大排列

 */
int order();
}

(2)每一個具體實現類,如BasicParamHandler、CommonParamHandler都實現了這個接口,里面具體的process()方法。

(3)在Maven工程的src/main/resources/下,創建META-INF/services/com.A.standard.chain.LogProcessHandler文件(UTF-8)
里面具體內容:

com.A.tools.BasicParamProcessHandler
com.A.tools.CommonParamProcessHandler
com.A.tools.TestParamProcessHandler

(4)主類中初始化

 /**
 * 初始化具體的處理類
 */
private void initConcreteHandler() {
    // 初始化
    handlers = new ArrayList<>();
    // 通過java spi機制load所有處理的hander
    ServiceLoader<LogProcessHandler> loader = ServiceLoader.load(LogProcessHandler.class);
    for (LogProcessHandler hander : loader ) {
        handlers.add(hander);
    }
    // handler 排序
    Collections.sort(handlers, new Comparator<LogProcessHandler>() {
        @Override
        public int compare(LogProcessHandler o1, LogProcessHandler o2) {
            return o1.order() - o2.order();
        }
    });
}

Ps:

一般情況下,是按照文件中實現類順序加載類,但是可能出現特殊情況。因此對集合中的實現類進行排序,這也是接口中定義了order方法的原因。

4. 原理

當服務的提供者,提供了服務接口的一種實現之后,在jar包的META-INF/services/目錄里同時創建一個以服務接口命名的文件。該文件里就是實現該服務接口的具體實現類。

當外部程序裝配這個模塊的時候,就能通過該jar包META-INF/services/里的配置文件找到具體的實現類名,並裝載實例化,完成模塊的注入。

基於這樣一個約定就能很好的找到服務接口的實現類,而不需要再代碼里制定。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM