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的使用需要三步操作
- 創建META-INF/services/service.name文件
- 將service的實現類的全限定名稱寫入該文件中
- 使用ServiceLoader.load(Service.class)來獲取實現類
不過其中還需要注意ClassLoader(其他篇幅說明)