目录
引言
Java的类加载机制是JVM实现模块化、动态扩展和安全性的核心基础,而双亲委派模型(Parent Delegation Model)是其核心设计原则。该机制通过类加载器之间的层级委托关系,确保了类加载的唯一性、安全性以及核心类库的不可篡改性。例如,即使开发者自定义java.lang.String 类,JVM仍会优先加载核心库中的版本,避免恶意代码注入。
1、双亲委派机制原理
1.1 类加载器层级
Java类加载器分为四层:
- 启动类加载器(Bootstrap ClassLoader):加载
JAVA_HOME/lib下的核心类库(如rt.jar),由C++实现,无父加载器。 - 扩展类加载器(Extension ClassLoader):加载
JAVA_HOME/lib/ext目录或java.ext.dirs指定的类库。 - 应用类加载器(Application ClassLoader):加载用户类路径(ClassPath)下的类,默认的类加载器。
- 自定义类加载器:用户继承
ClassLoader实现,用于特定场景(如热部署)。
1.2 委派流程
1.2.1 当一个类加载请求发生时,流程如下:
- 向上委托:子加载器将请求逐级委托给父加载器,直到启动类加载器。
- 父优先加载:若父加载器能完成加载(如核心类),直接返回结果。
- 子加载兜底:若父加载器无法加载(如用户自定义类),子加载器调用
findClass()自行加载。

1.2.2 优点:
- 安全性:防止核心类被篡改(如自定义
java.lang.Object无效)。 - 唯一性:避免重复加载导致类冲突。
- 资源隔离:不同加载器加载的类互不可见,支持模块化。
2、核心源码解析
以ClassLoader.loadClass() 方法为例,其实现体现了双亲委派逻辑:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 1. 检查是否已加载
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
// 2. 委托父加载器加载
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name); // 启动类加载器
}
} catch (ClassNotFoundException e) {}
// 3. 父加载失败,调用findClass()自行加载
if (c == null) {
c = findClass(name);
}
}
return c;
}
}
关键点:
- 递归委派:父加载器链式调用直至Bootstrap。
- findClass():需子类重写,定义自定义加载逻辑
3、自定义类加载器实践
3.1 实现步骤
- 继承
ClassLoader类。 - 重写相应方法,从指定路径读取字节码。
- 调用
defineClass()将字节数组转换为Class对象。
public class DynamicClassLoader extends ClassLoader {
// 使用WeakHashMap记录已加载类,允许GC回收
private final Map<String, Class<?>> loadedClasses = new WeakHashMap<>();
public DynamicClassLoader(ClassLoader parent) {
super(parent);
}
public DynamicClassLoader() {
super(getSystemClassLoader().getParent());
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 优先检查自定义加载的类
Class<?> c = loadedClasses.get(name);
if (name.startsWith("cn.ygc.demo.dto")) {
try {
// 强制重新加载目标包下的类
Path classPath = Paths.get("target/classes/" + name.replace('.', '/') + ".class");
if (Files.exists(classPath)) {
// 移除旧类缓存
if (c != null) {
loadedClasses.remove(name);
}
// 执行l类加载
c = reload(name, classPath);
}
} catch (Exception e) {
throw new ClassNotFoundException("Reload failed: " + name, e);
}
} else if (c == null) {
// 非目标包使用双亲委派
c = super.loadClass(name, resolve);
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
public Class<?> reload(String className, Path classPath) throws Exception {
byte[] bytes = Files.readAllBytes(classPath);
// 先移除旧类定义(如果存在)
if (findLoadedClass(className) != null) {
loadedClasses.remove(className);
}
Class<?> clazz = defineClass(className, bytes, 0, bytes.length);
loadedClasses.put(className, clazz);
return clazz;
}
}
3.2 测试用例
public static void main(String[] args) throws InterruptedException {
// 指定要加载的类(需提前编译生成.class文件)
String className = "cn.ygc.demo.dto.TestDemoDTO"; // 全限定类名
try {
// 创建动态加载器实例
DynamicClassLoader loader = new DynamicClassLoader();
// 执行类加载(核心调用)
Class<?> reloadedClass = loader.loadClass(className, true);
// 验证加载结果
System.out.println(" 新类哈希值:" + reloadedClass.hashCode());
Object instance = reloadedClass.getDeclaredConstructor().newInstance();
System.out.println(" 创建实例修改前:" + instance.toString());
Thread.sleep(10000);
// 修改TestDemoDTO后自动编译生成的.class文件
loader = new DynamicClassLoader();
// 执行类加载(核心调用)
reloadedClass = loader.loadClass(className, true);
// 验证加载结果
System.out.println(" 新类哈希值:" + reloadedClass.hashCode());
instance = reloadedClass.getDeclaredConstructor().newInstance();
System.out.println(" 创建实例修改后:" + instance.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
新类哈希值:1345636186
创建实例修改前:Testd3emoDTO{td3='null', name='null', age=null, sex='null', favorDemoList=null}
新类哈希值:25548982
创建实例修改后:Testd2emoDTO{td2='null', name='null', age=null, sex='null', favorDemoList=null}
4、应用场景与注意事项
- SPI机制:JDBC通过线程上下文类加载器加载第三方驱动。
- 热部署:Tomcat的WebAppClassLoader为每个应用独立加载类。
- 安全性:自定义加载器需谨慎,避免破坏沙箱机制。
5、总结
双亲委派机制通过层级化的类加载策略,奠定了Java安全与稳定的基石。理解其原理与源码实现,并结合自定义类加载器的灵活扩展,能够更好地应对模块化、动态加载等复杂场景。实际开发中,应根据需求权衡委派模型的利弊,合理设计类加载逻辑。
快速掌握 text/event-stream:开启实时通信新大门-优快云博客
Spring启动控制接口全解析_spring框架 启动端口-优快云博客
从JAR到EXE:Launch4j全流程打包指南与避坑实战-优快云博客
轻量级年会抽奖系统设计与实现——SpringBoot3.x+3D地球仪特效全解析-优快云博客
了解idea插件的开发流程及idea右键选择项目批量导出插件介绍-优快云博客
90

被折叠的 条评论
为什么被折叠?



