类加载器的双亲委派模型在JDK 1.2时期被引入,并被广泛应用于此后几乎所有的Java程序中,但它并不是一个具有强制性约束力的模型,而是Java设计者们推荐给开发者的一种类加载器实现的最佳 实践。
- Java中的类随着它的类 加载器一起具备了一种带有优先级的层次关系。
- 例如: java.lang.Object,java.lang.String 在程序的各种类加载器环境中都能够保证是同一个类,保证安全
- 若加载了别人写的java.lang.String,别人想干啥干啥
- 例如: java.lang.Object,java.lang.String 在程序的各种类加载器环境中都能够保证是同一个类,保证安全
下面若没特殊声明、默认都是讨论HotSpot JVM。
HotSpot的ClassLoader实现:
- 先检查请求加载的类型是否已经被加载过
- 若没有则调用父加载器的 loadClass()方法,
- 若父加载器为空则默认使用启动类加载器作为父加载器。
- 假如父类加载器加载失败, 抛出ClassNotFoundException异常的话,才调用自己的findClass()方法尝试进行加载。
HotSpot ClassLoader源码实现:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 步骤1:检查请求的类是否已经加载
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 如果父类加载器抛出ClassNotFoundException,说明父类加载器无法完成加载请求
}
if (c == null) {
//步骤2:在父类加载器无法加载时, 再调用本身的findClass方法来进行类加载
long t1 = System.nanoTime();
c = findClass(name);
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
在JVM实际运行时,运行机制如下:
自定义classloader->自定义classloader: findLoadedClass
自定义classloader->AppClassLoader: loadClass
AppClassLoader->AppClassLoader: findLoadedClass
AppClassLoader -> ExtClassLoader: loadClass
ExtClassLoader->ExtClassLoader: findLoadedClass
ExtClassLoader -> BootstrapClassLoader: loadClass
Note right of BootstrapClassLoader: 没有父加载器
BootstrapClassLoader -> BootstrapClassLoader : findBootstrapClassOrNull
Note right of BootstrapClassLoader: Bootstrap也失败,则调用findClass
BootstrapClassLoader -> BootstrapClassLoader : findClass
Note left of BootstrapClassLoader: 如果父类加载器抛出\nClassNotFoundException,\n说明父类加载器无法完成加载请求
BootstrapClassLoader --> ExtClassLoader: 若自己没找到,则委托孩子加载
ExtClassLoader -> ExtClassLoader : findClass
ExtClassLoader --> AppClassLoader: 若自己没找到,\n则委托孩子加载
AppClassLoader -> AppClassLoader : findClass
AppClassLoader --> 自定义classloader: 若自己没找到,\n则委托孩子加载
自定义classloader -> 自定义classloader : findClass
双亲委托模型并不是一个强制执行的模型,是Java设计者推荐开发者使用的类加载器实现方式。
截止到目前,双亲委派模型主要出现过3次较大规模“被破坏”的情况。
双亲委派模型在JDK 1.2之后才被引入,但是类加载器的概念和抽象类 java.lang.ClassLoader则在Java的第一个版本中就已经存在。
Java设计者们引入双亲委派模型时不得不做出一些妥协,为了兼容这些已有代码,无法再以技术 手段避免loadClass()被子类覆盖的可能性,只能在JDK 1.2之后的java.lang.ClassLoader中添加一个新的 protected方法findClass(),并引导用户编写的类加载逻辑时尽可能去重写这个方法,而不是在 loadClass()中编写代码。
以JDBC为例:
String url = "jdbc:mysql:///consult?serverTimezone=UTC";
String user = "root";
String password = "root";
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection(url, user, password);
- 深入理解JVM 周志明 第三版