线程上下文类加载器

(1)手动加载mysql驱动实现类

     1   Class<?> forName = Class.forName("com.mysql.jdbc.Driver");

     这时加载mysql实现类的是系统类加载器,执行上面的语句会将自己注册到DriverManager中的registeredDrivers。

     2  DriverManager.getConnection(url, username, password);

      获取数据库连接时候会遍历registeredDrivers,通过driver获取数据库连接并返回。

 (2)由系统帮我们加载mysql驱动实现类,这时候用到了线程上下文类加载器

            String url = "jdbc:mysql://127.0.0.1:3306/demo";
            String username = "root";
            String password = "root";
            //将这行注释掉也可以获取到连接
            // Class<?> forName = Class.forName("com.mysql.jdbc.Driver");
            Connection connection = DriverManager.getConnection(url, username, password);
            System.out.println(connection);
在调用DriverManager.getConnection(url, username, password)方法时,首先会有bootstrap类加载器加载DriverManager类,但是bootstrap类加载器不能加载classpath路径下的实现类,所以在该类的
静态块的load方法会获取线程上下文类加载器(appClassLoader),用来加载实现类
   public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

SPI的全名为Service Provider Interface,服务发现机制

Java SPI的具体约定为:当服务的提供者提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。基于这样一个约定就能很好的找到服务接口的实现类,而不需要在代码里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader。ServiceLoader最终要调用Class.forName(DriverName, false, loader)加载类,因为ServiceLoader.class是被BootrapLoader加载的,因此传给 forName 的 loader 必然不能是BootrapLoader,这时候父委派加载机就加载不了classpath下的实现类,这时候只能使用线程上下文类加载器(当应用启动时就设置了线程上下文类加载器是系统类加载器AppClassLoader),也就是说把自己加载不了的类由线程上下文类加载器加载。

 

 

原文地址:https://www.cnblogs.com/moris5013/p/11821676.html