• SPI : ServiceLoader如何打破双亲委派


    接上篇继续:SPI : Service Provider Interface

    ServiceLoader打破双亲委派


    我们知道jdk的核心API(e.g rt.jar)是BootstrapClassLoader加载的,三方提供的jar包是AppClassLoader加载的,那么ServiceLoader是rt.jar中的类,那么对应的加载器就是BootstrapClassLoader

    那么问题来了。

    如果一个类由类加载器加载,那么这个类依赖的类也是由相同的类加载器加载的。

    很显然,ServiceLoader这里打破了双亲委派机制


    如何做到的呢?
    写一段代码做个测试。

     public static void main(String[] args) {
         ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);
    
         Iterator<Driver> iterator = loader.iterator();
    
         while (iterator.hasNext()){
              Driver driver = iterator.next();
              System.out.println("driver:"+driver.getClass());
              System.out.println("classLoader:"+driver.getClass().getClassLoader());
          }
    
          System.out.println("current thread classLoader:"+Thread.currentThread().getContextClassLoader());
    
          System.out.println("ServiceLoader loader:"+ServiceLoader.class.getClassLoader());
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    我们在这个项目中引入了MySQL的jar包。执行结果如下:

    driver:class com.mysql.cj.jdbc.Driver
    classLoader:sun.misc.Launcher$AppClassLoader@18b4aac2
    current thread classLoader:sun.misc.Launcher$AppClassLoader@18b4aac2
    ServiceLoader loader:null
    
    • 1
    • 2
    • 3
    • 4

    从结果上我们可以看到,已经找到了MySQL的驱动。这里如果再引入Oracl的驱动的话,那么也会打印出来,就不做测试了。这其实就是JDK标准的SPI的一个特性:加载SPI接口的所有实现类。

    另外,从执行结果上我们可以看到ServiceLoader的加载器是BootStrapClassLoader,因为最后一行输出了null,并且从该类在rt.jar里边也可以说明这一点了。两种不同的classLoader说明传统的双亲委派确实被打破。

    ServiceLoader.load(Driver.class)入手找打破双亲委派的方式。
    在这里插入图片描述
    可以看到,第一行获取了当前线程上下文加载器,也就是AppClassLoader
    第二行做的其实就是通过构造方法将获得的cl赋值给ServiceLoader的成员变量loader

    // The class loader used to locate, load, and instantiate providers
    private final ClassLoader loader;
    
    • 1
    • 2

    那么这个成员变量loader是什么时候用的呢?

    我们调用next()方法的时候,这时候会触发一个类加载,由下边代码可以看到先是在Class.forName获取class的时候使用上边的成员变量loader,然后clazz.newInstance生成对象。
    在这里插入图片描述

    由此,JDK通过ServiceLoader实现的SPI打破了双亲委派

    尝试几句话说清楚:
    加载器是BootStrapClassLoaderServiceLoader在实例化本身时,将当前线程上上下文加载器赋值给本身的一个成员变量。后续需要实例化SPI接口时,通过这个成员变量去做实例化动作,从而实现了打破双亲委派。
    在这里插入图片描述

  • 相关阅读:
    SpringMVC实现文件上传和下载
    Java~常见的工具类 Collections、Arrays
    vue3.0+ant design vue3.2.6框架的基本使用
    登录远程SQLServer
    爬虫: response.text 爬取链接下的三国演义小说第一回,并将结果保存到桌面《三国演义》.txt文件中。
    ros2 -foxy安装cartographer建图定位-- 源码安装 使用
    【毕业设计】医学大数据分析 - 心血管疾病分析
    架构,平台,框架的区别和联系
    关联规则代码实现
    堪称一绝!阿里技术人都用的 Nginx 笔记手册,应用到架构齐全
  • 原文地址:https://blog.csdn.net/Paranoia_ZK/article/details/126569722