通過ServiceLoader實現鏈式處理

ServiceLoader與ClassLoader是Java中2個即相互區別又相互聯繫的加載器.JVM利用ClassLoader將類載入內存,這是一個類聲明週期的第一步(一個java類的完整的生命週期會經歷加載、連接、初始化、使用、和卸載五個階段,當然也有在加載或者連接之後沒有被初始化就直接被使用的情況)。詳情請參閱:詳解Java類的生命週期

ServiceLoader又是什麼呢?ServiceLoader:一個簡單的服務提供者加載設施。服務 是一個熟知的接口和類(通常爲抽象類)集合。服務提供者 是服務的特定實現。提供者中的類通常實現接口,並子類化在服務本身中定義的子類。服務提供者可以以擴展的形式安裝在 Java 平臺的實現中,也就是將 jar 文件放入任意常用的擴展目錄中。也可通過將提供者加入應用程序類路徑,或者通過其他某些特定於平臺的方式使其可用。……唯一強制要求的是,提供者類必須具有不帶參數的構造方法,以便它們可以在加載中被實例化。

通過在資源目錄META-INF/services中放置提供者配置文件 來標識服務提供者。文件名稱是服務類型的完全限定二進制名稱。該文件包含一個具體提供者類的完全限定二進制名稱列表,每行一個。忽略各名稱周圍的空格、製表符和空行。註釋字符爲'#'('\u0023', NUMBER SIGN);忽略每行第一個註釋字符後面的所有字符。文件必須使用 UTF-8 編碼。 

以延遲方式查找和實例化提供者,也就是說根據需要進行。服務加載器維護到目前爲止已經加載的提供者緩存。每次調用 iterator 方法返回一個迭代器,它首先按照實例化順序生成緩存的所有元素,然後以延遲方式查找和實例化所有剩餘的提供者,依次將每個提供者添加到緩存。可以通過 reload 方法清除緩存。

……

以上來源於Java API裏的說明,也許說的很專業,讓我們有點暈頭轉向,我們可以簡單的認爲:ServiceLoader也像ClassLoader一樣,能裝載類文件,但是使用時有區別,具體區別如下:(1) ServiceLoader裝載的是一系列有某種共同特徵的實現類,而ClassLoader是個萬能加載器;(2)ServiceLoader裝載時需要特殊的配置,使用時也與ClassLoader有所區別;(3)ServiceLoader還實現了Iterator接口。

下面是關於ServiceLoader的簡單的例子,僅供參考

(1)基礎服務:IService

1 package com.service;
2 public interface IService {
3     String sayHello();
4     String getScheme();
5 }


(2)具體服務實現1:HDFSService

01 package com.impl;
02 import com.service.IService;
03 public class HDFSService implements IService {
04     @Override
05     public String sayHello() {
06         return "Hello HDFSService";
07     }
08     @Override
09     public String getScheme() {
10         return "hdfs";
11     }
12 }


(3)具體服務實現2:LocalService

01 package com.impl;
02 import com.service.IService;
03 public class LocalService  implements IService {
04     @Override
05     public String sayHello() {
06         return "Hello LocalService";
07     }
08     @Override
09     public String getScheme() {
10         return "local";
11     }
12 }
(4)配置:META-INF/services/com.service.IService


1 com.impl.HDFSService
2 com.impl.LocalService
(5)測試類
01 package com.test;
02 import java.util.ServiceLoader;
03 import com.service.IService;
04 public class Test {
05     public static void main(String[] args) {
06         ServiceLoader<IService> serviceLoader  = ServiceLoader.load(IService.class);
07         for (IService service : serviceLoader) {
08             System.out.println(service.getScheme()+"="+service.sayHello());
09         }
10     }
11 }


結果:

hdfs=Hello HDFSService
local=Hello LocalService

可以看到ServiceLoader可以根據IService把定義的兩個實現類找出來,返回一個ServiceLoader的實現,而ServiceLoader實現了Iterable接口,所以可以通過ServiceLoader來遍歷所有在配置文件中定義的類的實例。


ServiceLoader的應用

(1)Hadoop FileSystem

Hadoop FileSystem就是通過這個機制來根據不同文件的scheme來返回不同的FileSystem。

01 private static void loadFileSystems() { 
02   synchronized (FileSystem.class) { 
03     if (!FILE_SYSTEMS_LOADED) { 
04       ServiceLoader<FileSystem> serviceLoader = ServiceLoader.load(FileSystem.class); 
05       for (FileSystem fs : serviceLoader) { 
06         SERVICE_FILE_SYSTEMS.put(fs.getScheme(), fs.getClass()); 
07       
08       FILE_SYSTEMS_LOADED = true
09     
10   
11 }
對應的配置文件:

1 org.apache.hadoop.fs.LocalFileSystem 
2 org.apache.hadoop.fs.viewfs.ViewFileSystem 
3 org.apache.hadoop.fs.s3.S3FileSystem 
4 org.apache.hadoop.fs.s3native.NativeS3FileSystem 
5 org.apache.hadoop.fs.kfs.KosmosFileSystem 
6 org.apache.hadoop.fs.ftp.FTPFileSystem 
7 org.apache.hadoop.fs.HarFileSystem
通過之前的測試類輸出對應的scheme和class如下: 
file=class org.apache.hadoop.fs.LocalFileSystem   
viewfs=class org.apache.hadoop.fs.viewfs.ViewFileSystem   
s3=class org.apache.hadoop.fs.s3.S3FileSystem   
s3n=class org.apache.hadoop.fs.s3native.NativeS3FileSystem   
kfs=class org.apache.hadoop.fs.kfs.KosmosFileSystem   
ftp=class org.apache.hadoop.fs.ftp.FTPFileSystem   
har=class org.apache.hadoop.fs.HarFileSystem   
hdfs=class org.apache.hadoop.hdfs.DistributedFileSystem   
hftp=class org.apache.hadoop.hdfs.HftpFileSystem   
hsftp=class org.apache.hadoop.hdfs.HsftpFileSystem   
webhdfs=class org.apache.hadoop.hdfs.web.WebHdfsFileSystem  

可以看到FileSystem會把所有的FileSystem的實現都以scheme和class來cache,之後就從這個cache中取相應的值。因此,以後可以通過ServiceLoader來實現一些類似的功能,而不用依賴像Spring這樣的第三方框架。

(2)責任鏈模式

責任鏈模式的定義:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。

責任連模式可以使用ServiceLoader實現具體服務對象的迭代加載並處理,爲了確保此模式的靈活性,建議判斷邏輯通過配置文件或數據庫的方式,具體實現方式見 參考鏈接(2) 消滅成堆的……


參考資料:

(1)java.util.ServiceLoader使用

(2)消滅成堆的分支語句之類責任鏈模式 

(3)轉一篇很不錯的介紹NetBeans的文章

附:測試用例文件的文件結構


轉自:http://my.oschina.net/hanzhankang/blog/109794

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章