ServiceLoader源碼

ServiceLoader源碼

最近在研發過程中無意中閱讀到了Flink的TableFactoryService的源碼,其中提供一個find方法,用於實現:通過接口類型來獲取所有的實現類列表。跟蹤代碼,發現是基於JDK提供的ServiceLoader實現的,所以簡單做一下記錄。


過程說明

  • Flink接口:org.apache.flink.table.factories.TableFactory
  • Flink接口擴展實現類之一:org.apache.flink.formats.json.JsonRowFormatFactory
  • ServiceLoader.load(org.apache.flink.table.factories.TableFactory.class, …)來獲取對應的實現類
    • mkdir -p META-IND/servives && cd META-INF/services
    • cat org.apache.flink.formats.json.JsonRowFormatFactory > org.apache.flink.formats.json.JsonRowFormatFactory
    • 然後 通過ServiceLoader.load(org.apache.flink.table.factories.TableFactory.class, …) 就可以獲的JsonRowFormatFactory的實例對象

源碼分析

public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) {
    return new ServiceLoader<>(service, loader);
}

iterator = serviceLoader.iterator(); // 獲取迭代對象

// iterator.hasNext方法會調用hashNextService
private boolean hasNextService() {
    if (nextName != null) { return true; }
    if (configs == null) {
    	// service.getName() -- 全限定名稱
    	String fullName = "META-INF/services/" + service.getName();
    	configs = ClassLoader.getSystemResources(fullName);
    }
    while ((pending == null) || !pending.hasNext()) {
        if (!configs.hasMoreElements()) { return false; }
        pending = parse(service, configs.nextElement());
    }
    nextName = pending.next();
    return true;
}

private Iterator<String> parse(Class<?> service, URL u) throws ServiceConfigurationError {
    InputStream in = u.openStream();
    BufferedReader r = new BufferedReader(new InputStreamReader(in, "utf-8"));
    ArrayList<String> names = new ArrayList<>();
    int lc = 1;
    // 獲取每一行數據(代表一個實現類的全限定名稱)
    while ((lc = parseLine(service, u, r, lc, names)) >= 0);
    return names.iterator();
}

// iterator.next()方法會調用nextService
private S nextService() {
    String cn = nextName; // META-INF/services/service.name 中的一行數據 => 一個實現類的全限定名稱
    Class<?> c = Class.forName(cn, false, loader); // 裝載實現類
	S p = service.cast(c.newInstance());
    providers.put(cn, p);
    return p;
}

總結

ServiceLoader的使用需要三步操作

  1. 創建META-INF/services/service.name文件
  2. 將service的實現類的全限定名稱寫入該文件中
  3. 使用ServiceLoader.load(Service.class)來獲取實現類

不過其中還需要注意ClassLoader(其他篇幅說明)

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