ServiceLoader與ClassLoader是Java中2個即相互區別又相互聯繫的加載器.JVM利用ClassLoader將類載入內存,這是一個類聲明週期的第一步(一個java類的完整的生命週期會經歷加載、鏈接、初始化、使用、和卸載五個階段,固然也有在加載或者鏈接以後沒有被初始化就直接被使用的狀況)。詳情請參閱:詳解Java類的生命週期 html
那ServiceLoader又是什麼呢?ServiceLoader:一個簡單的服務提供者加載設施。服務 是一個熟知的接口和類(一般爲抽象類)集合。服務提供者 是服務的特定實現。提供者中的類一般實現接口,並子類化在服務自己中定義的子類。服務提供者能夠以擴展的形式安裝在 Java 平臺的實現中,也就是將 jar 文件放入任意經常使用的擴展目錄中。也可經過將提供者加入應用程序類路徑,或者經過其餘某些特定於平臺的方式使其可用。……惟一強制要求的是,提供者類必須具備不帶參數的構造方法,以便它們能夠在加載中被實例化。 java
經過在資源目錄META-INF/services中放置提供者配置文件 來標識服務提供者。文件名稱是服務類型的徹底限定二進制名稱。該文件包含一個具體提供者類的徹底限定二進制名稱列表,每行一個。忽略各名稱周圍的空格、製表符和空行。註釋字符爲'#'('\u0023', NUMBER SIGN);忽略每行第一個註釋字符後面的全部字符。文件必須使用 UTF-8 編碼。
linux
以延遲方式查找和實例化提供者,也就是說根據須要進行。服務加載器維護到目前爲止已經加載的提供者緩存。每次調用 iterator 方法返回一個迭代器,它首先按照實例化順序生成緩存的全部元素,而後以延遲方式查找和實例化全部剩餘的提供者,依次將每一個提供者添加到緩存。能夠經過 reload 方法清除緩存。 web
…… 數據庫
以上來源於Java API裏的說明,也許說的很專業,讓咱們有點暈頭轉向,咱們能夠簡單的認爲:ServiceLoader也像ClassLoader同樣,能裝載類文件,可是使用時有區別,具體區別以下:(1) ServiceLoader裝載的是一系列有某種共同特徵的實現類,而ClassLoader是個萬能加載器;(2)ServiceLoader裝載時須要特殊的配置,使用時也與ClassLoader有所區別;(3)ServiceLoader還實現了Iterator接口。[若有錯誤或不到的地方敬請指出,互相學習:)] apache
下面是關於ServiceLoader的簡單的例子,僅供參考 緩存
(1)基礎服務:IService 框架
package com.service; public interface IService { String sayHello(); String getScheme(); }
(2)具體服務實現1:HDFSService ide
package com.impl; import com.service.IService; public class HDFSService implements IService { @Override public String sayHello() { return "Hello HDFSService"; } @Override public String getScheme() { return "hdfs"; } }
(3)具體服務實現2:LocalService oop
package com.impl; import com.service.IService; public class LocalService implements IService { @Override public String sayHello() { return "Hello LocalService"; } @Override public String getScheme() { return "local"; } }(4)配置:META-INF/services/com.service.IService
com.impl.HDFSService com.impl.LocalService(5)測試類
package com.test; import java.util.ServiceLoader; import com.service.IService; public class Test { public static void main(String[] args) { ServiceLoader<IService> serviceLoader = ServiceLoader.load(IService.class); for (IService service : serviceLoader) { System.out.println(service.getScheme()+"="+service.sayHello()); } } }
結果:
hdfs=Hello HDFSService
local=Hello LocalService
能夠看到ServiceLoader能夠根據IService把定義的兩個實現類找出來,返回一個ServiceLoader的實現,而ServiceLoader實現了Iterable接口,因此能夠經過ServiceLoader來遍歷全部在配置文件中定義的類的實例。
ServiceLoader的應用
(1)Hadoop FileSystem
Hadoop FileSystem就是經過這個機制來根據不一樣文件的scheme來返回不一樣的FileSystem。
private static void loadFileSystems() { synchronized (FileSystem.class) { if (!FILE_SYSTEMS_LOADED) { ServiceLoader<FileSystem> serviceLoader = ServiceLoader.load(FileSystem.class); for (FileSystem fs : serviceLoader) { SERVICE_FILE_SYSTEMS.put(fs.getScheme(), fs.getClass()); } FILE_SYSTEMS_LOADED = true; } } }對應的配置文件:
org.apache.hadoop.fs.LocalFileSystem org.apache.hadoop.fs.viewfs.ViewFileSystem org.apache.hadoop.fs.s3.S3FileSystem org.apache.hadoop.fs.s3native.NativeS3FileSystem org.apache.hadoop.fs.kfs.KosmosFileSystem org.apache.hadoop.fs.ftp.FTPFileSystem org.apache.hadoop.fs.HarFileSystem經過以前的測試類輸出對應的scheme和class以下:
能夠看到FileSystem會把全部的FileSystem的實現都以scheme和class來cache,以後就從這個cache中取相應的值。所以,之後能夠經過ServiceLoader來實現一些相似的功能,而不用依賴像Spring這樣的第三方框架。
(2)責任鏈模式
責任鏈模式的定義:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。
責任連模式可使用ServiceLoader實現具體服務對象的迭代加載並處理,爲了確保此模式的靈活性,建議判斷邏輯經過配置文件或數據庫的方式,具體實現方式見 參考連接(2) 消滅成堆的……
參考資料:
附:測試用例文件的文件結構