鴻蒙OS ServiceLoader

2022-08-04 17:10 更新

ServiceLoader

java.lang.Object

|---java.util.ServiceLoader<S&

public final class ServiceLoader<S>
extends Object
implements Iterable<S>

一個簡單的服務提供者加載工具。

服務是一組眾所周知的接口和(通常是抽象的)類。 服務提供者是服務的具體實現(xiàn)。 提供者中的類通常實現(xiàn)服務本身中定義的類的接口和子類。 服務提供者可以以擴展的形式安裝在 Java 平臺的實現(xiàn)中,即將 jar 文件放置在任何常用的擴展目錄中。 還可以通過將提供程序添加到應用程序的類路徑或通過某些其他特定于平臺的方式來使提供程序可用。

出于加載的目的,服務由單一類型表示,即單一接口或抽象類。 (可以使用具體類,但不建議這樣做。)給定服務的提供者包含一個或多個具體類,這些具體類使用提供者特定的數(shù)據(jù)和代碼擴展此服務類型。 提供者類通常不是整個提供者本身,而是一個代理,它包含足夠的信息來決定提供者是否能夠滿足特定請求以及可以按需創(chuàng)建實際提供者的代碼。 提供者類的細節(jié)往往是高度特定于服務的; 沒有一個類或接口可以統(tǒng)一它們,所以這里沒有定義這樣的類型。 此工具強制執(zhí)行的唯一要求是提供程序類必須具有零參數(shù)構造函數(shù),以便它們可以在加載期間被實例化。

通過在資源目錄 META-INF/services 中放置提供者配置文件來識別服務提供者。 該文件的名稱是服務類型的完全限定二進制名稱。 該文件包含具體提供程序類的完全限定二進制名稱列表,每行一個。 每個名稱周圍的空格和制表符以及空白行將被忽略。 注釋字符為'#'('\u0023', NUMBER SIGN); 在每一行中,第一個注釋字符之后的所有字符都將被忽略。 該文件必須以 UTF-8 編碼。

如果一個特定的具體提供者類在多個配置文件中被命名,或者在同一個配置文件中被命名不止一次,那么重復的將被忽略。 命名特定提供者的配置文件不必與提供者本身位于相同的 jar 文件或其他分發(fā)單元中。 必須可以從最初查詢配置文件的同一個類加載器訪問提供程序; 請注意,這不一定是實際加載文件的類加載器。

提供者的定位和實例化是惰性的,即按需。 服務加載器維護到目前為止已加載的提供程序的緩存。 迭代器方法的每次調(diào)用都會返回一個迭代器,它首先按實例化順序產(chǎn)生緩存的所有元素,然后延遲定位并實例化任何剩余的提供者,依次將每個提供者添加到緩存中。 可以通過 reload 方法清除緩存。

服務加載程序總是在調(diào)用者的安全上下文中執(zhí)行。 受信任的系統(tǒng)代碼通常應該從特權安全上下文中調(diào)用此類中的方法以及它們返回的迭代器的方法。

此類的實例對于多個并發(fā)線程的使用是不安全的。

除非另有說明,否則將 null 參數(shù)傳遞給此類中的任何方法都將導致拋出 NullPointerException。

示例 假設我們有一個服務類型 com.example.CodecSet,它旨在表示某些協(xié)議的編碼器/解碼器對集合。 在這種情況下,它是一個具有兩個抽象方法的抽象類:

 public abstract Encoder getEncoder(String encodingName);
 public abstract Decoder getDecoder(String encodingName);

如果提供者不支持給定的編碼,每個方法都會返回一個適當?shù)膶ο蠡?null。 典型的供應商支持不止一種編碼。

如果 com.example.impl.StandardCodecs 是 CodecSet 服務的實現(xiàn),那么它的 jar 文件還包含一個名為

 META-INF/services/com.example.CodecSet

該文件包含一行:

 com.example.impl.StandardCodecs    # Standard codecs

CodecSet 類在初始化時創(chuàng)建并保存單個服務實例:

 private static ServiceLoader<CodecSet> codecSetLoader
     = ServiceLoader.load(CodecSet.class);

為了找到給定編碼名稱的編碼器,它定義了一個靜態(tài)工廠方法,該方法遍歷已知和可用的提供程序,僅在找到合適的編碼器或用完提供程序時返回。

 public static Encoder getEncoder(String encodingName) {
     for (CodecSet cp : codecSetLoader) {
         Encoder enc = cp.getEncoder(encodingName);
         if (enc != null)
             return enc;
     }
     return null;
 }

getDecoder 方法的定義類似。

使用說明 如果用于加載提供程序的類加載器的類路徑包含遠程網(wǎng)絡 URL,則在搜索提供程序配置文件的過程中將取消引用這些 URL。

此活動是正常的,盡管它可能會導致在 Web 服務器日志中創(chuàng)建令人費解的條目。但是,如果未正確配置 Web 服務器,則此活動可能會導致提供程序加載算法虛假失敗。

當請求的資源不存在時,Web 服務器應返回 HTTP 404(未找到)響應。但是,有時 Web 服務器被錯誤地配置為在這種情況下返回 HTTP 200 (OK) 響應以及有用的 HTML 錯誤頁面。當此類嘗試將 HTML 頁面解析為提供程序配置文件時,這將導致拋出 ServiceConfigurationError。此問題的最佳解決方案是修復錯誤配置的 Web 服務器以返回正確的響應代碼 (HTTP 404) 以及 HTML 錯誤頁面。

方法總結

修飾符和類型 方法 描述
IteratorS iterator() 延遲加載此加載程序服務的可用提供程序。
static <S> ServiceLoader<S> load(Class<S> service) 使用當前線程的上下文類加載器為給定的服務類型創(chuàng)建一個新的服務加載器。
static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) 為給定的服務類型和類加載器創(chuàng)建一個新的服務加載器。
static <S> ServiceLoader<S> loadInstalled(Class<S> service) 使用擴展類加載器為給定的服務類型創(chuàng)建一個新的服務加載器。
void reload() 清除此加載器的提供程序緩存,以便重新加載所有提供程序。
String toString() 返回描述此服務的字符串。
從接口 java.lang.Iterable 繼承的方法
forEach, spliterator
從類 java.lang.Object 繼承的方法
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

方法詳情

reload

public void reload()

清除此加載器的提供程序緩存,以便重新加載所有提供程序。

調(diào)用此方法后,迭代器方法的后續(xù)調(diào)用將懶惰地從頭開始查找并實例化提供程序,就像新創(chuàng)建的加載程序所做的那樣。

此方法適用于可以將新提供程序安裝到正在運行的 Java 虛擬機中的情況。

iterator

public IteratorS iterator()

延遲加載此加載程序服務的可用提供程序。

此方法返回的迭代器首先按實例化順序生成提供程序緩存的所有元素。 然后它會延遲加載并實例化任何剩余的提供者,依次將每個提供者添加到緩存中。

為了實現(xiàn)惰性,解析可用提供程序配置文件和實例化提供程序的實際工作必須由迭代器本身完成。 因此,如果提供程序配置文件違反了指定格式,或者如果它命名了無法找到和實例化的提供程序類,或者如果實例化類的結果不能分配給服務類型,則它的 hasNext 和 next 方法可以拋出 ServiceConfigurationError ,或者在定位和實例化下一個提供程序時引發(fā)任何其他類型的異?;蝈e誤。 要編寫健壯的代碼,只需要在使用服務迭代器時捕獲 ServiceConfigurationError。

如果拋出這樣的錯誤,那么迭代器的后續(xù)調(diào)用將盡最大努力定位和實例化下一個可用的提供程序,但通常不能保證這樣的恢復。

設計說明 在這些情況下拋出錯誤可能看起來很極端。 這種行為的基本原理是格式錯誤的提供程序配置文件(如格式錯誤的類文件)表明 Java 虛擬機的配置或使用方式存在嚴重問題。 因此,最好拋出錯誤而不是嘗試恢復,或者更糟糕的是,靜默失敗。

此方法返回的迭代器不支持移除。 調(diào)用它的 remove 方法將導致拋出 UnsupportedOperationException。

指定者:

接口 IterableS 中的迭代器

返回:

延遲加載此加載器服務的提供程序的迭代器

load

public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader)

為給定的服務類型和類加載器創(chuàng)建一個新的服務加載器。

類型參數(shù):

類型參數(shù)名稱 類型參數(shù)描述
S 服務類型的類

參數(shù):

參數(shù)名稱 參數(shù)描述
service 表示服務的接口或抽象類
loader 用于加載提供程序配置文件和提供程序類的類加載器,如果要使用系統(tǒng)類加載器(或者,如果失敗,則為引導類加載器),則為 null

返回:

一個新的服務加載器

load

public static <S> ServiceLoader<S> load(Class<S> service)

使用當前線程的上下文類加載器為給定的服務類型創(chuàng)建一個新的服務加載器。

調(diào)用表單的這種便捷方法

 ServiceLoader.load(service)

相當于

 ServiceLoader.load(service,
                    Thread.currentThread().getContextClassLoader())

類型參數(shù):

類型參數(shù)名稱 類型參數(shù)描述
S 服務類型的類

參數(shù):

參數(shù)名稱 參數(shù)描述
service 表示服務的接口或抽象類

返回:

一個新的服務加載器

loadInstalled

public static <S> ServiceLoader<S> loadInstalled(Class<S> service)

使用擴展類加載器為給定的服務類型創(chuàng)建一個新的服務加載器。

這個方便的方法只是簡單的定位擴展類加載器,調(diào)用它extClassLoader,然后返回

 ServiceLoader.load(service, extClassLoader)

如果找不到擴展類加載器,則使用系統(tǒng)類加載器; 如果沒有系統(tǒng)類加載器,則使用引導類加載器。

此方法旨在僅在需要已安裝的提供程序時使用。 生成的服務只會查找并加載已安裝到當前 Java 虛擬機中的提供程序; 應用程序類路徑上的提供者將被忽略。

類型參數(shù):

類型參數(shù)名稱 類型參數(shù)描述
S 服務類型的類

參數(shù):

參數(shù)名稱 參數(shù)描述
service 表示服務的接口或抽象類

返回:

一個新的服務加載器

toString

public String toString()

返回描述此服務的字符串。

覆蓋:

類 Object 中的 toString

返回:

描述性字符串

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號