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(其他篇幅说明)