java双亲委派机制
java双亲委派机制
Java的双亲委派机制(Parent Delegation Model)是类加载器(ClassLoader)在加载类时采用的一种工作模型。它是Java类加载机制的核心设计之一,主要用于保证类的唯一性和安全性。以下是双亲委派机制的详细说明:
1. 什么是双亲委派机制?
双亲委派机制是指当一个类加载器收到类加载请求时,它不会立即尝试加载该类,而是将请求委派给父类加载器去完成。每一层的类加载器都会依次向上委派,直到启动类加载器(Bootstrap ClassLoader)。如果父类加载器无法完成加载(在自己的搜索范围内找不到该类),子类加载器才会尝试加载。
2. 双亲委派机制的工作流程
收到类加载请求 :
- 当一个类加载器收到类加载请求时,首先检查该类是否已经被加载过。
- 如果已经加载过,则直接返回对应的
Class
对象。
委派给父类加载器 :
- 如果该类未被加载过,类加载器会将加载请求委派给父类加载器。
- 父类加载器会重复同样的过程,继续向上委派,直到启动类加载器。
父类加载器尝试加载 :
- 如果父类加载器可以完成加载,则返回对应的
Class
对象。 - 如果父类加载器无法完成加载(在自己的搜索范围内找不到该类),则子类加载器会尝试加载。
- 如果父类加载器可以完成加载,则返回对应的
子类加载器加载 :
- 如果子类加载器也无法加载该类,则抛出
ClassNotFoundException
。
- 如果子类加载器也无法加载该类,则抛出
3. 双亲委派机制的类加载器层次结构
Java中的类加载器按照层次结构组织,主要包括以下几种类加载器:
启动类加载器(Bootstrap ClassLoader) :
- 负责加载JVM核心类库(如
rt.jar
),由C++实现,是JVM的一部分。 - 是其他类加载器的顶级父加载器。
- 负责加载JVM核心类库(如
扩展类加载器(Extension ClassLoader) :
- 负责加载
<JAVA_HOME>/lib/ext
目录下的类库。 - 由
sun.misc.Launcher$ExtClassLoader
实现。
- 负责加载
应用程序类加载器(Application ClassLoader) :
- 负责加载用户类路径(
classpath
)下的类库。 - 由
sun.misc.Launcher$AppClassLoader
实现。
- 负责加载用户类路径(
自定义类加载器(Custom ClassLoader) :
- 用户可以通过继承
java.lang.ClassLoader
类实现自定义类加载器。 - 自定义类加载器的父加载器是应用程序类加载器。
- 用户可以通过继承
4. 双亲委派机制的优点
避免类的重复加载 :
- 通过委派机制,可以确保一个类只会被加载一次,从而避免类的重复加载。
保护核心类库的安全 :
- 核心类库(如
java.lang
包)由启动类加载器加载,用户无法通过自定义类加载器篡改核心类库。
- 核心类库(如
保证类的唯一性 :
- 同一个类在不同的类加载器加载时会被认为是不同的类,双亲委派机制可以确保类的唯一性。
5. 双亲委派机制的实现
双亲委派机制的实现主要体现在
ClassLoader
的
loadClass
方法中。以下是
loadClass
方法的简化逻辑:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// 1. 检查类是否已经被加载
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
// 2. 委派给父类加载器
if (parent != null) {
c = parent.loadClass(name, false);
} else {
// 3. 如果父类加载器为null,则委派给启动类加载器
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 父类加载器无法加载
}
if (c == null) {
// 4. 父类加载器无法加载时,自己尝试加载
c = findClass(name);
}
}
// 5. 如果需要解析,则解析该类
if (resolve) {
resolveClass(c);
}
return c;
}
6. 打破双亲委派机制的场景
在某些特殊场景下,可能需要打破双亲委派机制,例如:
SPI(Service Provider Interface) :
- 如JDBC驱动加载,核心类库(如
java.sql.DriverManager
)需要调用用户实现的类(如com.mysql.cj.jdbc.Driver
)。 - 通过
Thread.currentThread().getContextClassLoader()
获取线程上下文类加载器来加载用户类。
- 如JDBC驱动加载,核心类库(如
OSGi :
- OSGi是一个模块化框架,每个模块有自己的类加载器,模块之间通过类加载器隔离。
热部署 :
- 在应用运行时动态加载新的类文件,通常需要自定义类加载器。
7. 示例:双亲委派机制的工作过程
假设有以下类加载器层次结构:
- 启动类加载器(Bootstrap ClassLoader)
- 扩展类加载器(Extension ClassLoader)
- 应用程序类加载器(Application ClassLoader)
当应用程序类加载器收到加载
java.lang.String
的请求时:
- 应用程序类加载器将请求委派给扩展类加载器。
- 扩展类加载器将请求委派给启动类加载器。
- 启动类加载器成功加载
java.lang.String
,并返回Class
对象。
如果加载的是用户自定义类
com.example.MyClass
:
- 应用程序类加载器将请求委派给扩展类加载器。
- 扩展类加载器将请求委派给启动类加载器。
- 启动类加载器无法加载
com.example.MyClass
,扩展类加载器也无法加载。 - 应用程序类加载器尝试加载,并成功加载
com.example.MyClass
。
8. 总结
双亲委派机制是Java类加载机制的核心设计,通过层次化的类加载器结构和委派机制,保证了类的唯一性、安全性和一致性。