简单的理解双亲加载机制就是,当一个类加载器收到类加载请求的时候,首先不会自己去加载, 而是委派父加载器完成,如果父加载器无法完成加载,子加载器才会自己完成加载。
JVM中包括集中类加载器:
- BootStrapClassLoader 引导类加载器
- ExtClassLoader 扩展类加载器
- AppClassLoader 应用类加载器
- 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的要求。 而通过给当前线程设置上下文类加载器,就可以设置的上下文类加载器来实现对于接口实现类的加载