Java ServiceLoader與Spring工廠載入器

banq發表於2018-07-18
現在Spring boot來到了Jar世界,原來它是管理war世界的王者,所以,他也很可能帶來了Jar打包規則的改變。Jar世界從OSGI到Java Service Loader以及Java 9的模組化,折騰了很長時間,現在看看Spring是如何折騰Jar包的動態載入的:

控制反轉IOC不僅可以在類級別進行,而且可以在模組級別進行,雖然OSGi技術已經存世了很長時間了,但是,在Java中也可以像在Spring中一般都可以直接使用IoC方法來實現類似OSGI方式。

Java Service Loader
這是來自JDK的開箱即用的Java API,它提供了一種特定方式的控制反轉IOC,由Service Loader類實現。它用來定位類路徑上的某個介面的實現類。這種方式能夠讓我們在Java執行時動態發現類路徑classpath上指定介面的某個實現,動態工廠載入模式,這樣就分離了API模組和它的多個具體實現模組類,也就是說,同一套API介面,我們後面可以給予它不同的實現方式,這種介面和實現的清晰分離一直是Java生態最強的優勢。

日誌框架SLF4J就選擇了這條路徑。SLF4J本身就是API,但可以使用不同的實現(如Logback,Log4J等),SLF4J客戶端僅與SLF4J API這個抽象介面模組互動,而放置在類路徑上的不同的實現模組在執行時能夠以不同方式處理細節。

Service Loader有一個約束,需要Jar包裡存在一個目錄META-INF/services目錄,在這個目錄裡有一個檔案,檔案的名稱完全是介面的完整路徑名稱,而其內容則是指定介面的實現類完整路徑名稱。例如,對於介面com.Foo,必須有一個名為META-INF/services/com.Foo檔案,檔案內容如下所示的檔案:

com.FooImpl1
com.FooImpl2

上面的類都必須實現com.Foo介面。

從程式碼的角度來看,它非常簡單:

ServiceLoader<Foo> loader = ServiceLoader.load(Foo.class);
loader.iterator();
<p class="indent">

上面程式碼獲得loader是一個集合類,需要遍歷一次。


Service Loader與Spring整合
我們可以透過工廠bean將上面ServiceLoader與Spring整合,例如以下程式碼:

@Configuration
public class ServiceConfiguration {

    @Bean
    public ServiceListFactoryBean serviceListFactoryBean() {
        ServiceListFactoryBean serviceListFactoryBean = new ServiceListFactoryBean();
        serviceListFactoryBean.setServiceType(Foo.class);
        return serviceListFactoryBean;
    }
}

Object object = serviceListFactoryBean.getObject();
<p class="indent">


顯然,這需要進一步的操作才能以正確的形式獲取資料(提示:它是一個連結串列)。

Spring Factories Loader
與Java Service Loader一樣,Spring提供了另一個反轉控制的實現,但是隻涉及一個屬性檔案,它必須被命名spring.factories並位於Jar其下META-INF目錄下。從程式碼的角度來看,該檔案是透過SpringFactoriesLoader.loadFactories() 這個靜態方法讀取的- 是的,對於Spring來說,它非常強悍。

客戶端程式碼無法更簡單:

List<Foo> foos = SpringFactoriesLoader.loadFactories(Foo.class, null);
<p class="indent">


注意第二個引數是可選的類載入器。

與Java Service Loader相比,差異有兩方面:

1. 一種檔案格式是否比其他檔案格式更好?更可讀或更易於維護,這也許是個人品味的問題。

2. spring.factories的關鍵是不需要介面,也不需要實現子類來實現它。Spring Boot就是使用這種方法來處理自動配置bean:關鍵就只是一個註釋,即 org.springframework.boot.autoconfigure.EnableAutoConfiguration,值是在類上面的註解@Configuration中寫明的,這能夠有更靈活的設計。

以上完整程式碼:github

Java Service Loader vs Spring Factories Loader

相關文章