JavaSPI 機制學(xué)習(xí)筆記

最近在閱讀框架源代碼時,常??吹?SPI 的子包, 忍不住查了下:Service Provider Interface : 服務(wù)提供接口。
JavaSPI 實際上是“基于接口的編程+策略模式+配置文件”組合實現(xiàn)的動態(tài)加載機制。具體而言:
STEP1. 定義一組接口, 假設(shè)是 autocomplete.PrefixMatcher;
? ? ??
STEP2. 寫出接口的一個或多個實現(xiàn)(autocomplete.EffectiveWordMatcher, autocomplete.SimpleWordMatcher);
? ? ??
STEP3. 在 src/main/resources/ 下建立 /META-INF/services 目錄, 新增一個以接口命名的文件 autocomplete.PrefixMatcher, 內(nèi)容是要應(yīng)用的實現(xiàn)類(autocomplete.EffectiveWordMatcher 或 autocomplete.SimpleWordMatcher 或兩者);
? ? ??
STEP4. 使用 ServiceLoader 來加載配置文件中指定的實現(xiàn)。?
SPI 的應(yīng)用之一是可替換的插件機制。比如查看 JDBC 數(shù)據(jù)庫驅(qū)動包,mysql-connector-java-5.1.18.jar 就有一個 /META-INF/services/java.sql.Driver 里面內(nèi)容是 com.mysql.jdbc.Driver 。

? ?
代碼示例:
?
1. 編寫接口和實現(xiàn)類:autocomplete.PrefixMatcher, ?autocomplete.EffectiveWordMatcher, autocomplete.SimpleWordMatcher 見 《輸入自動提示與補全功能的設(shè)計與實現(xiàn)》;
2. 在 src/main/resources/ 下建立文件 /META-INF/services/ autocomplete.PrefixMatcher 填入上述兩個類之一或兩者都填;
3. 編寫測試類。? ? ??
package autocomplete;
import?java.util.Iterator;
import?java.util.ServiceLoader;
/**
?* Created by lovesqcc on 16-2-29.
?*/
public?class?PrefixMatcherTest {
????public?static?void?main(String[] args) {
????????ServiceLoader matcher = ServiceLoader.load(PrefixMatcher.class);
????????Iterator matcherIter = matcher.iterator();
????????while?(matcherIter.hasNext()) {
????????????PrefixMatcher wordMatcher = matcherIter.next();
????????????System.out.println(wordMatcher.getClass().getName());
????????????String[] prefixes = new?String[] {"a", "b", "c", "d", "e", "f", "g", "i",
????????????????????"l", "n", "p", "r", "s", "t", "v", "w", "do", "finally"};
????????????for?(String?prefix: prefixes) {
????????????????System.out.println(wordMatcher.obtainMatchedWords(prefix));
????????????}
????????}
????}
} 要寫個 ServiceLoader 的簡單實現(xiàn)也不難:1.
讀取配置文件,獲取實現(xiàn)類的全名稱字符串;2. 使用 Java 反射機制來構(gòu)造服務(wù)實現(xiàn)類的實例。可以使用泛型方法,避免獲取的時候做類型轉(zhuǎn)換。不過
JDK 自帶的 java.util.ServiceLoader 實現(xiàn)得更加嚴(yán)謹(jǐn)一些,使用了 ClassLoader
來加載類,并使用迭代器來獲取服務(wù)實現(xiàn)類。思路大體相同。
package autocomplete;
import?java.io.*;
import?java.util.ArrayList;
import?java.util.List;
/**
?* Created by lovesqcc on 16-2-29.
?* A very Simple JavaSPI implementation using java reflection
?*/
public?class?SimpleServiceLoader {
????private?static?final String?PREFIX = "/META-INF/services/";
????public?static? List load(Class cls) {
????????List<String> implClasses = readServiceFile(cls);
????????List implList = new?ArrayList();
????????for?(String?implClass : implClasses) {
????????????Class c = null;
????????????try?{
????????????????c = (Class) Class.forName(implClass);
????????????????implList.add(c.newInstance());
????????????} catch?(Exception e) {
????????????????return?new?ArrayList();
????????????}
????????}
????????return?implList;
????}
????private?static?List<String> readServiceFile(Class> cls) {
????????String?infName = cls.getCanonicalName();
????????String?fileName = cls.getResource(PREFIX+infName).getPath();
????????try?{
????????????BufferedReader br = new?BufferedReader(new?FileReader(new?File(fileName)));
????????????String?line = "";
????????????List<String> implClasses = new?ArrayList<String>();
????????????while?((line = br.readLine()) != null) {
????????????????implClasses.add(line);
????????????}
????????????return?implClasses;
????????} catch?(FileNotFoundException fnfe) {
????????????System.out.println("File not found: "?+ fileName);
????????????return?new?ArrayList<String>();
????????} catch?(IOException ioe) {
????????????System.out.println("Read file failed: "?+ fileName);
????????????return?new?ArrayList<String>();
????????}
????}
????public?static?void?main(String[] args) {
????????List implList = load(PrefixMatcher.class);
????????if?(implList != null?&& implList.size() >0) {
????????????for?(PrefixMatcher matcher: implList) {
????????????????System.out.println(matcher.obtainMatchedWords("sh"));
????????????}
????????}
????}
} ServiceLoader 的實現(xiàn)涉及到如下概念:指向?qū)ο箢愋偷?
Class 對象;類加載器 ClassLoader;服務(wù)實現(xiàn)類的資源抽象;服務(wù)實現(xiàn)類的全名字符串。結(jié)合類加載器和資源抽象獲得服務(wù)實現(xiàn)類的全名字符串,再通過類加載器獲取 Class 對象, 最后通過
Class 對象來構(gòu)造服務(wù)實現(xiàn)類 S 的實例 s 。
ServiceLoader 的實現(xiàn)主要包括:
? ? ??
1. ?內(nèi)部成員包含 ? ? ?? 2. LazyIterator 的內(nèi)部成員包括 在 Java 中,Class 關(guān)注GitHub今日熱榜,專注挖掘好用的開發(fā)工具,致力于分享優(yōu)質(zhì)高效的工具、資源、插件等,助力開發(fā)者成長! 點個在看 你最好看
