3.1 ServiceLoader 的成员变量先看一下 ServiceLoader 类的成员变量,大致有个印象,后面的源码中都会使用到 。
public final class ServiceLoader<S> implements Iterable<S> {// SPI 配置文件目录private static final String PREFIX = "META-INF/services/";// 将要被加载的 SPI 服务private final Class<S> service;// 用于加载 SPI 服务的类加载器private final ClassLoader loader;// ServiceLoader 创建时的访问控制上下文private final AccessControlContext acc;// SPI 服务缓存,按实例化的顺序排列private LinkedHashMap<String,S> providers = new LinkedHashMap<>();// 懒查询迭代器private LazyIterator lookupIterator;// ...}3.2 ServiceLoader 的工作流程(1)ServiceLoader.load 静态方法
应用程序加载 Java SPI 服务,都是先调用 ServiceLoader.load 静态方法 。
ServiceLoader.load 静态方法的作用是:
① 指定类加载 ClassLoader 和访问控制上下文;
② 然后,重新加载 SPI 服务
- 清空缓存中所有已实例化的 SPI 服务
- 根据 ClassLoader 和 SPI 类型,创建懒加载迭代器
// service 传入的是期望加载的 SPI 接口类型// loader 是用于加载 SPI 服务的类加载器public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) {return new ServiceLoader<>(service, loader);}public void reload() {// 清空缓存中所有已实例化的 SPI 服务providers.clear();// 根据 ClassLoader 和 SPI 类型,创建懒加载迭代器lookupIterator = new LazyIterator(service, loader);}// 私有构造方法// 重新加载 SPI 服务private ServiceLoader(Class<S> svc, ClassLoader cl) {service = Objects.requireNonNull(svc, "Service interface cannot be null");// 指定类加载 ClassLoader 和访问控制上下文loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;// 然后,重新加载 SPI 服务reload();}(2)应用程序通过 ServiceLoader 的 iterator 方法遍历 SPI 实例
ServiceLoader 的类定义,明确了 ServiceLoader 类实现了 Iterable<T> 接口,所以,它是可以迭代遍历的 。实际上,ServiceLoader 类维护了一个缓存 providers( LinkedHashMap 对象),缓存 providers 中保存了已经被成功加载的 SPI 实例,这个 Map 的 key 是 SPI 接口实现类的全限定名,value 是该实现类的一个实例对象 。
当应用程序调用 ServiceLoader 的 iterator 方法时,ServiceLoader 会先判断缓存 providers 中是否有数据:如果有,则直接返回缓存 providers 的迭代器;如果没有,则返回懒加载迭代器的迭代器 。
public Iterator<S> iterator() {return new Iterator<S>() {// 缓存 SPI providersIterator<Map.Entry<String,S>> knownProviders= providers.entrySet().iterator();// lookupIterator 是 LazyIterator 实例,用于懒加载 SPI 实例public boolean hasNext() {if (knownProviders.hasNext())return true;return lookupIterator.hasNext();}public S next() {if (knownProviders.hasNext())return knownProviders.next().getValue();return lookupIterator.next();}public void remove() {throw new UnsupportedOperationException();}};}(3)懒加载迭代器的工作流程
上面的源码中提到了,lookupIterator 是 LazyIterator 实例,而 LazyIterator 用于懒加载 SPI 实例 。那么,LazyIterator 是如何工作的呢?
这里,摘取 LazyIterator 关键代码
hasNextService 方法:
- 拼接 META-INF/services/ + SPI 接口全限定名
- 通过类加载器,尝试加载资源文件
- 解析资源文件中的内容,获取 SPI 接口的实现类的全限定名 nextName
nextService 方法:经验总结扩展阅读
- 10月28日3时42分新疆阿克苏地区沙雅县发生3.0级地震
- 10月28日4时34分新疆阿克苏地区温宿县发生3.0级地震
- 今年第22号台风“尼格”最强可达台风级 30日傍晚前后进入南海海面
- 红米note9pro评测最新_红米note9pro深度测评
- 空调一级能效与三级能效的区别是什么
- iOS 16.3.1正式版续航怎么样 iOS 16.3.1升级建议
- ipx7和ip68哪个更防水 哪个防水等级更高
- 10月28日今起三天陕西持续阴雨模式局地阵风6级 风寒效应明显需保暖
- EasyPoi大数据导入导出百万级实例
- Dubbo-聊聊通信模块设计