SPI 是 Java 提供的一種服務(wù)加載方式,全名為 Service Provider Interface泻拦,可以避免在 Java 代碼中寫(xiě)死服務(wù)的提供者烈疚,而是通過(guò) SPI 服務(wù)加載機(jī)制進(jìn)行服務(wù)的注冊(cè)和發(fā)現(xiàn)。通過(guò)這種方式聪轿,可以基于接口編程爷肝,實(shí)現(xiàn)多個(gè)模塊的解耦。
SPI 機(jī)制實(shí)現(xiàn)解耦
如下的示例展示了通過(guò) ServiceLoader 類(lèi)加載指定接口的所有服務(wù)提供者并進(jìn)行調(diào)用的簡(jiǎn)單實(shí)現(xiàn)陆错。
1灯抛、定義接口 test.DirMonitor,包含一個(gè)方法 start()音瓷;
2对嚼、實(shí)現(xiàn)接口 test.DirMonitor,定義兩個(gè)實(shí)現(xiàn)類(lèi) test.ObserverMonitor 和 test.LoopMonitor绳慎;
3纵竖、設(shè)置接口的實(shí)現(xiàn)類(lèi)列表。創(chuàng)建目錄 META-INF/services/杏愤,新建文件 test.DirMonitor靡砌,內(nèi)容如下:
test.ObserverMonitor
test.LoopMonitor
4、在程序中通過(guò) ServiceLoader 類(lèi)加載 test.DirMonitor 接口的實(shí)現(xiàn)類(lèi)珊楼,并遍歷所有實(shí)現(xiàn)類(lèi)通殃,調(diào)用 start() 方法;
從上面的示例可以看出厕宗,在代碼中僅僅使用到了接口 test.DirMonitor画舌,并沒(méi)有在代碼中使用到具體實(shí)現(xiàn)類(lèi)堕担。通過(guò)這種方法,可以實(shí)現(xiàn)解耦曲聂,接口與實(shí)現(xiàn)類(lèi)可以由不同的開(kāi)發(fā)人員實(shí)現(xiàn)洋闽,編譯到不同的 jar 包中泡徙,甚至實(shí)現(xiàn)插件的定義與開(kāi)發(fā)宙址。
spi 包的本地化擴(kuò)展
java.util.spi 包提供了一些抽象類(lèi)廊遍,可以用于擴(kuò)展 Java 的本地化服務(wù)。本地化 SPI 的使用方法與 ServiceLoader 的 SPI 稍有不同乍丈,本地化 SPI 的實(shí)現(xiàn)需要打包成 jar 包后剂碴,放置于運(yùn)行的 jre/lib/ext 目錄下方能生效。java.util.spi 包提供的抽象類(lèi)如下所示:
CalendarDataProvider:為 java.util.Calendar 類(lèi)的參數(shù)提供本地化數(shù)據(jù)的服務(wù)提供者的抽象類(lèi)轻专;
CalendarNameProvider:為 java.util.Calendar 類(lèi)的字段提供本地化名稱(chēng)的服務(wù)提供者的抽象類(lèi)忆矛;
CurrencyNameProvider:為 java.util.Currency 提供本地化貨幣符號(hào)名稱(chēng)的服務(wù)提供者的抽象類(lèi);
LocaleNameProvider:為 java.util.Locale 類(lèi)提供本地化名稱(chēng)的服務(wù)提供者的抽象類(lèi)请垛;
LocaleServiceProvider:其他服務(wù)提供者抽象類(lèi)的基類(lèi)催训;
ResourceBundleControlProvider:服務(wù)接口,用于提供 java.util.ResourceBundle.Control 的實(shí)現(xiàn)類(lèi)宗收;
TimeZoneNameProvider:為 java.util.TimeZone 提供本地化時(shí)區(qū)的服務(wù)提供者的抽象類(lèi)漫拭。
以 CalendarDataProvider 為例,該抽象類(lèi)用于實(shí)現(xiàn)本地化的日歷數(shù)據(jù)混稽。在下面的例子中采驻,CalendarDataProviderSPI 類(lèi)設(shè)置了每周的第一天為 3,一年中第一周所需的最少天數(shù)為 6匈勋。
按照 SPI 的要求礼旅,在 META-INF/services/ 下建立 java.util.spi.CalendarDataProvider 文件,并寫(xiě)入 testspi.CalendarDataProviderSPI洽洁。打包為 jar 后痘系,放置于 jre/lib/ext 目錄后,執(zhí)行下面的代碼饿自,則打印的數(shù)字分別為 3 和 6汰翠,而不再是默認(rèn)的 1 和 1。
總結(jié)
Java 通過(guò) SPI 機(jī)制為我們提供了一種服務(wù)發(fā)現(xiàn)機(jī)制昭雌。通過(guò)在 META-INF/services/ 目錄下創(chuàng)建以接口全限定名為名稱(chēng)的文件复唤,并在文件中每行寫(xiě)入一個(gè)服務(wù)提供者的全限定名,Java 可以發(fā)現(xiàn)并創(chuàng)建服務(wù)提供者的實(shí)例城豁。
通過(guò) ServiceLoader 我們可以獲取指定接口的所有服務(wù)提供者苟穆,并實(shí)現(xiàn)接口調(diào)用與服務(wù)提供者的解耦抄课。通過(guò)實(shí)現(xiàn) spi 包的抽象接口唱星,并放置于 jre/lib/ext 目錄下雳旅,可以實(shí)現(xiàn) Java 的本地化。