經過ServiceLoader實現鏈式處理

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以下:
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的文章

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

相關文章
相關標籤/搜索