最近在一個日誌標準化的項目中,使用了責任鏈模式來連接每個具體的處理Handler.可是在實例化時,須要每個都去建立實例。
如:java
/** * 初始化具體的處理類 */ private void initConcreteHandler() { handlers.add(new BasicParamHandler()); handlers.add(new CommonParamHandler()); handlers.add(new TestParamHandler()); …… }
這種,涉及了具體的實現類,就違反了可拔插的原則,若是須要替換一種實現,就須要修改代碼。老大看到個人代碼,直接讓用Java spi機制去作。mysql
Service Provider Interface:服務提供者接口.例如,系統裏抽象的各個模塊,每每有不少不一樣的實現方案,好比日誌模塊的方案,xml解析模塊、jdbc模塊的方案等。sql
面向的對象的設計裏,咱們通常推薦模塊之間基於接口編程,模塊之間不對實現類進行硬編碼。爲了實如今模塊裝配的時候能不在程序裏動態指明,這就須要一種服務發現機制。在模塊化設計中這個機制尤爲重要。apache
java spi就是提供這樣的一個機制:爲某個接口尋找服務實現的機制。編程
apache最先提供的日誌的門面接口。只有接口,沒有實現。具體方案由各提供商實現, 發現日誌提供商是經過掃描 META-INF/services/org.apache.commons.logging.LogFactory配置文件,經過讀取該文件的內容找到日誌提工商實現類。只要咱們的日誌實現裏包含了這個文件,並在文件裏制定 LogFactory工廠接口的實現類便可。ide
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
(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(); } }); }
通常狀況下,是按照文件中實現類順序加載類,可是可能出現特殊狀況。所以對集合中的實現類進行排序,這也是接口中定義了order方法的緣由。
當服務的提供者,提供了服務接口的一種實現以後,在jar包的META-INF/services/目錄裏同時建立一個以服務接口命名的文件。該文件裏就是實現該服務接口的具體實現類。
當外部程序裝配這個模塊的時候,就能經過該jar包META-INF/services/裏的配置文件找到具體的實現類名,並裝載實例化,完成模塊的注入。
基於這樣一個約定就能很好的找到服務接口的實現類,而不須要再代碼裏制定。