2017-07-29 90 views
0

我想了解Java的ServiceLoader的概念,工作機制和具體的使用案例,但找到官方documentation太抽象和混亂。Java服務加載器說明

首先,文件概述了服務服務提供商。 服務是一組打包在jar文件(API庫)中的接口和抽象類。服務提供者是一組實現或擴展API的類,它們封裝在一個獨特的jar文件(提供程序庫)中。


到目前爲止好,但隨後文檔變得混亂。

爲了加載,服務由單一類型表示,即單個接口或抽象類。 (可以使用具體類,但不建議這樣做。)給定服務的提供者包含一個或多個具體類,這些類可以擴展此服務類型,並提供特定於提供者的數據和代碼。提供者類通常不是整個提供者本身,而是包含足夠信息的代理,以確定提供者是否能夠滿足特定請求以及可以按需創建實際提供者的代碼。提供者類的細節往往是高度服務特定的;沒有一個類或接口可以統一它們,所以這裏沒有定義這種類型。該工具強制執行的唯一要求是提供程序類必須具有零參數構造函數,以便在加載期間可以對其進行實例化。

那麼究竟是服務類型Provider類?我得到的印象是,服務類型是API庫中的facade,提供者類是提供程序庫中實現此Facade接口的實例,實際上這個類是ServiceLoader加載的。它是否正確?但對我來說,它仍然沒有什麼意義,所有的組件如何連接在一起。

提供者類是一個代理,它決定提供者是否能夠滿足特定請求以及可以按需創建實際提供者的代碼?沒有統一的類型可以定義在哪裏?基本上所有這一段都是令人困惑的,我希望通過一個具體的例子來聽取更多可理解的解釋。


然後約供應商配置文件 ...

服務提供商通過將供應商的配置文件中的資源目錄META-INF /服務標識。該文件的名稱是服務類型的完全限定二進制名稱。該文件包含具體提供程序類的完全限定二進制名稱列表,每行一個 ...

命名特定提供程序的配置文件不需要與提供程序本身位於同一個jar文件或其他分發單元中。提供者必須可以從最初查詢的相同類加載器訪問以查找配置文件;請注意,這不一定是實際加載文件的類加載器。

這是否意味着服務類型爲org.foo的API。BarServiceType,類路徑中必須存在提供者jar,其中包含實現此類型的類META-INF/services/org.foo.BarServiceType命名的提供程序配置文件列出了此提供程序類,它們都可以通過相同的Classloader訪問,其中哪些代碼可以加載ServiceLoader以在API上查找和綁定提供程序?

從類加載器的角度來看,可訪問意味着提供程序配置文件和提供程序庫可以在程序包之外提供,位於層次結構的上方,即來自容器或其他中間件。

提供程序配置文件列出提供程序類,並可能捆綁在提供程序包中(爲什麼它會列出多個類,如果捆綁?)或來自外部。但是更常見的方法是:在提供者中提供配置文件,還是從API庫本身提供列出一組受支持的提供者的文件?或者後者是一種誤解?


最後約ServiceLoader

ServiceLoader實際實例化,並呼籲加載服務提供商?這是否發生在由API庫提供的工廠方法中?例如,LoggingFactory.getLogger(clazz)SLF4J內部委託給ServiceLoader,它使用反射來讀取提供者配置文件並加載服務?

服務加載機制如何處理情況,有多個提供者的配置文件存在,還是存在提供程序配置文件條目,但不包含類本身?

除了日誌框架之外還有其他一些ServiceLoader的具體用例嗎?它在多大程度上被廣泛使用在流行的框架之下,如Java EE,Spring and Hibernate?有什麼替代服務加載機制與鬆散耦合的API提供程序綁定,或者有?

回答

2

服務類型是傳遞給ServiceLoader.load或ServiceLoader.loadInstalled的接口或抽象類。提供者是該接口或抽象類的具體實現。

由於服務通常由大量的功能組成,因此如果在ServiceLoader掃描這些大類時並不是所有這些大類都立即加載的話。相反,更好的設計是一個可以訪問主要功能的小型類。例如,ServiceLoader.load(FileSystemProvider.class)不加載能夠處理特定文件系統集的整個庫;相反,它加載一個FileSystemProvider對象,該對象能夠初始化該庫,當且僅當該應用程序選擇使用它時。這允許提供者本身保持輕量級。

這是否意味着與org.foo.BarServiceType的服務類型的API,在類路徑中有一定存在一類供應商罐子實現這種類型和META-INF /服務/ org.foo.BarServiceType命名提供程序配置文件列出了這個提供程序類,所有這些都可以通過加載ServiceLoader在API上查找和綁定提供程序的同一個Classloader訪問?

是的。通常這很簡單;例如,包含org.foo的.jar文件。BarServiceType類還包含一個META-INF/services/org.foo.BarServiceType條目,其內容由一行文本組成。

爲什麼它會列出多個類,如果捆綁?

某些服務提供商只能處理某些情況。一個例子是IIORegistry類(它沒有提到ServiceLoader,事實上,在ServiceLoader被添加到Java SE之前,它的功能與ServiceLoader的功能相同)。可能有ImageReaderSpi的一個實現,它爲PNG提供ImageReader,爲ImageReader提供JPEG的另一個ImageReaderSpi等等。每個這樣的服務提供者類(即ImageReaderSpi的每個具體實現)在其canDecodeInput方法中將具有不同的邏輯,因此除非應用程序實際需要它們,否則不會創建重量級ImageReader實例。

但是哪種方法更爲常見:在提供程序中提供配置文件,還是從API庫本身提供列出一組受支持的提供程序的文件?

如果我正確理解你的問題,答案是在實踐中,SPI描述符總是與它命名的提供程序類在同一個.jar文件中。

至於你的問題的最後部分:我不認爲記錄器框架使用ServiceLoader。有關使用ServiceLoader的示例,請查看以.spi(java.awt.im.spi,java.nio.channels.spi,java.nio.charset.spi等)結尾的所有Java SE軟件包。他們中的大多數人並沒有說他們依賴於ServiceLoader,但他們都描述了他們的查找行爲,並且您會發現它幾乎總是與ServiceLoader相同。

服務加載機制如何處理情況,是否存在多個提供程序及其配置文件存在,或者存在提供程序配置文件條目但不包含類本身?

在類路徑中存在多個提供程序的情況下,ServiceLoader將簡單地返回其所有Iterator中的所有提供程序。

對於錯誤的配置文件,從ServiceLoader的Iterator的next()方法引發ServiceConfigurationError。從文檔:

加載服務提供商時出現錯誤時引發錯誤。

該錯誤將在以下情況下被拋出:

  • 提供者配置文件的格式違反規範;
  • 讀取提供程序配置文件時發生IOException;
  • 無法找到提供程序配置文件中指定的具體提供程序類;
  • 具體的提供者類不是服務類的子類;
  • 一個具體的提供者類不能被實例化;或
  • 發生其他一些類型的錯誤。

總結:

  • 服務提供商實現類通常是相同的.jar的META-INF /服務描述符文件。
  • 服務提供者類通常是一個輕量級類,它允許應用程序訪問更重的類,當且僅當應用程序決定需要它們時。
  • Java SE的很多部分都使用ServiceLoader,尤其是* .spi包。
  • 如果服務有很多實現,所有都在迭代器中返回。
  • 不正確的描述符導致ServiceConfigurationError。
+0

真的很有幫助。但我仍然發現一件令人困惑的事情:「服務提供者類通常是一個輕量級類,它允許應用程序訪問更重的類,當且僅當應用程序決定需要它時」。這是否僅僅意味着它加載的類是完整API的外觀,還是'proxy'在這裏有更多的動態行爲 –

+0

它既不是façade也不是代理。這有點像工廠。例如,[XPathFactory](https://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html)不是[XPath]的外觀或代理(https:/ /docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPath.html)實例;它是獲取XPath實例的手段。 – VGR