JavaSPI详解( 三 )

当执行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()一样 , 本质都是创建了一个ServiceLoaderd对象,不同点是使用的加载器不同,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 使用及原理分析

推荐阅读