Java spi機制

01. Java SPI機制

最近在一個日誌標準化的項目中,使用了責任鏈模式來連接每個具體的處理Handler.可是在實例化時,須要每個都去建立實例。
如:java

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

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

這種,涉及了具體的實現類,就違反了可拔插的原則,若是須要替換一種實現,就須要修改代碼。老大看到個人代碼,直接讓用Java spi機制去作。mysql

1. SPI機制簡介

Service Provider Interface:服務提供者接口.例如,系統裏抽象的各個模塊,每每有不少不一樣的實現方案,好比日誌模塊的方案,xml解析模塊、jdbc模塊的方案等。sql

面向的對象的設計裏,咱們通常推薦模塊之間基於接口編程,模塊之間不對實現類進行硬編碼。爲了實如今模塊裝配的時候能不在程序裏動態指明,這就須要一種服務發現機制。在模塊化設計中這個機制尤爲重要。apache

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

2. 實現案例

1.common-logging

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

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.FabricMySQLDriverurl

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/裏的配置文件找到具體的實現類名,並裝載實例化,完成模塊的注入。

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

相關文章
相關標籤/搜索