JVM

类双亲加载机制

Posted by Clear Blog on May 26, 2017

简单的理解双亲加载机制就是,当一个类加载器收到类加载请求的时候,首先不会自己去加载, 而是委派父加载器完成,如果父加载器无法完成加载,子加载器才会自己完成加载。

JVM中包括集中类加载器:

  1. BootStrapClassLoader 引导类加载器
  2. ExtClassLoader 扩展类加载器
  3. AppClassLoader 应用类加载器
  4. CustomClassLoader 用户自定义类加载器

再说说破坏双亲委派模式的典型案例吧。 JDBC驱动加载就是典型,Tomcat的类加载机制也是违背了双亲委派模型。 以JDBC为例,先执行以下一段代码

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
    Enumeration<Driver> drivers = DriverManager.getDrivers();
    Driver driver;
    while (drivers.hasMoreElements())
    {
        driver = drivers.nextElement();
        System.out.println(driver.getClass() + "------" + driver.getClass().getClassLoader());
    }
    System.out.println("DriverManager classLoader:" + DriverManager.class.getClassLoader());
}

只会打印null。然后添加mysql-driver

1
2
3
4
5
6
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <!--<version>8.0.14</version>-->
    <version>5.1.45</version>
</dependency>

打印内容如下:

1
2
3
class com.mysql.jdbc.Driver-----sun.misc.Launcher$AppClassLoader@18b4aac2
class com.mysql.fabric.jdbc.FabricMySQLDriver-----sun.misc.Launcher$AppClassLoader@18b4aac2
DriverManager classLoader:null

若A类调用B类,则B类由A类的加载器加载, 也就是说启动类加载器要加载jar包下的类,我们都知道这是不可能的, 启动类加载器负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class, 而打印出来的两个驱动是哪来的呢,查看源码

1
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);

使用的是Jdk提供的SPI机制实现的。 JDBC通过Thread.currentThread().getContextClassLoader()得到线程上下文加载器来加载Driver实现类。

在双亲委托模型下,类加载器是由下至上的,即下层的类加载器会委托上层进行加载。 但是对于SPI来说,有些接口是JAVA核心库提供的,而JAVA核心库是由启动类加载器来加载的, 而这些接口的实现却来自于不同的jar包(厂商提供),JAVA的启动类加载器是不会加载其他来源的jar包, 这样传统的双亲委托模型就无法满足SPI的要求。 而通过给当前线程设置上下文类加载器,就可以设置的上下文类加载器来实现对于接口实现类的加载