当执行hasNext()
方法时 , 会先去providers
查找已经加载的缓存实现,如果不存在,则会继续调用LazyIterator#hasNext()
用于发现尚未加载的实现,最后的实现在LazyIterator#hasNextService()
中
LazyIterator
的关键属性
// 缓存所有需要查找jar包(文件)路径,Enumeration<URL> configs = null;// 缓存所有被查找到的实现类全限定名Iterator<String> pending = null;// 迭代器使用 , 下一个需要被加载的类全限定名String nextName = null;hasNextService()
实现核心如下:
// 获取所由需要扫描的包路径configs = loader.getResources(fullName);// 循环扫描configs中所有的包路径,解析META-INF/services中的指定文件(上例中的cn.bmilk.chat.spi.Registry文件)//while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } // pending缓存了所有查找到的类全限定名 pending = parse(service, configs.nextElement());}在知道是否存在接口的实现后,就是通过next()
方法获取实现,核心功能由nextService()
贡献,核心实现如下:
// 获取一个实现类全限定名String cn = nextName;// 加载这个类Class<?> c = Class.forName(cn, false, loader);// 使用反射创建对象c.newInstance()hasNextService()
完成堆配置文件的读?。?code>nextService()完成类的加载和对象的创建,这个一切都没有在ServiceLoader
创建时完成,这也是体现了延迟Lazy
的一个含义
load()与loadInstalled()loadInstalled()
和load()
一样 , 本质都是创建了一个ServiceLoader
d对象,不同点是使用的加载器不同,load()
使用的是Thread.currentThread().getContextClassLoader()
当前线程的上下文加载器 , loadInstalled()
使用的是ExtClassLoader
加载器来加载
具体实现如下:
public static <S> ServiceLoader<S> loadInstalled(Class<S> service) { ClassLoader cl = ClassLoader.getSystemClassLoader(); ClassLoader prev = null; while (cl != null) { prev = cl; cl = cl.getParent(); } return ServiceLoader.load(service, prev); }使用这个方法将只扫描JDK
安装目录jre/lib/ext
下的jar
包中指定的实现,我们应用程序类路径下的实现将会被忽略掉
Java SPI的问题
Java SPI
虽然使用了懒加载机制,但是其获取一个实现时,需要使用迭代器循环加载所有的实现类
- 当需要某一个实现类时,需要通过循环一遍来获取
Dubbo
实现自己的SPI
机制时进行了增强,可以仅加载自己想要的扩展实现 。为什么SPI机制打破了双亲委派模型 ??想不明白 说不清楚,想明白再补充
参考资料
- Java SPI 使用及原理分析
推荐阅读
- 九 Istio:istio安全之授权
- 分布式ID生成方案总结整理
- GCC 指令详解及动态库、静态库的使用
- 五 Istio:使用服务网格Istio进行流量路由
- pta第二次博客
- java中GC的日志认识详解
- 即兴小探华为开源行业领先大数据虚拟化引擎openLooKeng
- JUC中的AQS底层详细超详解
- 图文详解 微服务 Zipkin 链路追踪原理
- JDK中自带的JVM分析工具