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

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