当前位置: 首页 > news >正文

JAVA--双亲委派机制

目录

什么是双亲委派机制

类加载器的层级结构

1. 启动类加载器(Bootstrap ClassLoader)

2. 扩展类加载器(Extension ClassLoader)

3. 应用程序类加载器(Application ClassLoader)

4. 自定义类加载器(Custom ClassLoader)

双亲委派机制的工作原理

为什么需要双亲委派机制

1. 避免类的重复加载

2. 保证Java核心API的安全性

3. 保证类的唯一性

双亲委派机制的源码分析

双亲委派机制的破坏

1. 自定义类加载器

2. 线程上下文类加载器

3. 热替换和热部署

实际应用场景

1. Web应用服务器

2. OSGi框架

3. 模块化系统

总结


什么是双亲委派机制

双亲委派机制(Parents Delegation Model)是JVM中类加载器的一种工作机制。当一个类加载器收到类加载请求时,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成。只有当父类加载器无法完成加载请求时,子类加载器才会尝试自己去加载。

这种机制确保了Java核心API的类不会被随意替换,维护了Java运行环境的安全性和稳定性。

类加载器的层级结构

Java中的类加载器按照层级关系分为以下几种:

1. 启动类加载器(Bootstrap ClassLoader)

  • 位置:JVM内部实现,由C++代码实现
  • 作用:加载Java核心类库(如java.lang.*java.util.*等)
  • 路径$JAVA_HOME/lib目录下的类库
  • 特点:最顶层的类加载器,没有父类加载器

2. 扩展类加载器(Extension ClassLoader)

  • 位置sun.misc.Launcher$ExtClassLoader
  • 作用:加载扩展类库
  • 路径$JAVA_HOME/lib/ext目录下的类库
  • 父类加载器:启动类加载器

3. 应用程序类加载器(Application ClassLoader)

  • 位置sun.misc.Launcher$AppClassLoader
  • 作用:加载应用程序类路径(ClassPath)上的类
  • 路径:环境变量ClassPath指定的路径
  • 父类加载器:扩展类加载器
  • 特点:也称为系统类加载器

4. 自定义类加载器(Custom ClassLoader)

  • 作用:用户根据需要自定义的类加载器
  • 父类加载器:通常是应用程序类加载器
// 查看类加载器层级结构的示例代码
public class ClassLoaderHierarchy {public static void main(String[] args) {// 获取当前类的类加载器ClassLoader classLoader = ClassLoaderHierarchy.class.getClassLoader();System.out.println("当前类的类加载器:" + classLoader);// 获取父类加载器ClassLoader parentClassLoader = classLoader.getParent();System.out.println("父类加载器:" + parentClassLoader);// 获取祖父类加载器ClassLoader grandParentClassLoader = parentClassLoader.getParent();System.out.println("祖父类加载器:" + grandParentClassLoader);// 输出结果:// 当前类的类加载器:sun.misc.Launcher$AppClassLoader@2a139a55// 父类加载器:sun.misc.Launcher$ExtClassLoader@15db9742// 祖父类加载器:null (Bootstrap ClassLoader由C++实现,在Java中显示为null)}
}

双亲委派机制的工作原理

双亲委派机制的工作流程如下:

  1. 接收加载请求:类加载器接收到类加载请求
  2. 向上委派:不立即加载,而是委派给父类加载器
  3. 递归委派:父类加载器继续向上委派,直到启动类加载器
  4. 尝试加载:启动类加载器尝试加载类
  5. 向下返回:如果加载失败,返回给子类加载器尝试加载
  6. 最终加载:直到某个类加载器成功加载类或全部失败
graph TDA[自定义类加载器] --> B[应用程序类加载器]B --> C[扩展类加载器]C --> D[启动类加载器]D --> E{能否加载?}E -->|能| F[加载完成]E -->|不能| G[委派给子类加载器]G --> H{扩展类加载器能否加载?}H -->|能| I[加载完成]H -->|不能| J[委派给子类加载器]J --> K{应用程序类加载器能否加载?}K -->|能| L[加载完成]K -->|不能| M[委派给子类加载器]M --> N{自定义类加载器能否加载?}N -->|能| O[加载完成]N -->|不能| P[抛出ClassNotFoundException]

为什么需要双亲委派机制

1. 避免类的重复加载

如果没有双亲委派机制,每个类加载器都可能加载同一个类,导致内存中存在多个相同的类对象。

2. 保证Java核心API的安全性

防止核心API被恶意替换。例如,如果有人自定义了一个java.lang.String类,通过双亲委派机制,最终会由启动类加载器加载JDK中的String类,而不是用户自定义的类。

3. 保证类的唯一性

在JVM中,类的唯一性是由类加载器和类的全限定名共同决定的。双亲委派机制确保了同一个类只会被同一个类加载器加载一次。

双亲委派机制的源码分析

让我们来看看ClassLoader类中loadClass方法的实现:

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// 首先检查该类是否已经被加载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) {// 如果父类加载器无法加载,则调用自己的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;}
}

双亲委派机制的破坏

虽然双亲委派机制很重要,但在某些场景下需要被破坏:

1. 自定义类加载器

通过重写loadClass方法来改变类加载的行为:

public class CustomClassLoader extends ClassLoader {@Overrideprotected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {// 首先检查是否已经加载Class<?> c = findLoadedClass(name);if (c == null) {// 对于自定义的类,直接由当前类加载器加载if (name.startsWith("com.example.")) {c = findClass(name);} else {// 其他类仍然遵循双亲委派c = super.loadClass(name, resolve);}}if (resolve) {resolveClass(c);}return c;}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {// 实现自定义的类加载逻辑byte[] classData = loadClassData(name);return defineClass(name, classData, 0, classData.length);}private byte[] loadClassData(String name) {// 从自定义位置加载类的字节码// 这里可以从网络、数据库等位置加载return null; // 简化示例}
}

2. 线程上下文类加载器

在某些情况下,父类加载器需要加载由子类加载器加载的类,这时可以使用线程上下文类加载器:

public class ContextClassLoaderExample {public static void main(String[] args) {// 获取当前线程的上下文类加载器ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();System.out.println("上下文类加载器:" + contextClassLoader);// 设置自定义的上下文类加载器Thread.currentThread().setContextClassLoader(new CustomClassLoader());// 在某些框架中,会使用上下文类加载器来加载类// 例如:JDBC驱动加载、Spring容器等}
}

3. 热替换和热部署

在开发环境中,为了实现热替换功能,需要破坏双亲委派机制:

public class HotSwapClassLoader extends ClassLoader {@Overrideprotected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {Class<?> c = findLoadedClass(name);if (c == null) {// 对于需要热替换的类,每次都重新加载if (isHotSwapClass(name)) {c = findClass(name);} else {c = super.loadClass(name, resolve);}}if (resolve) {resolveClass(c);}return c;}private boolean isHotSwapClass(String name) {// 判断是否是需要热替换的类return name.startsWith("com.example.hotswap");}
}

实际应用场景

1. Web应用服务器

Tomcat等Web服务器为了实现应用隔离,每个Web应用都有自己的类加载器:

// Tomcat的类加载器层级结构
// Bootstrap ClassLoader
//     |
// System ClassLoader
//     |
// Common ClassLoader
//     |
// Catalina ClassLoader  Shared ClassLoader
//                           |
//                      WebApp ClassLoader

2. OSGi框架

OSGi框架完全破坏了双亲委派机制,实现了网状的类加载器结构。

3. 模块化系统

Java 9的模块系统也对双亲委派机制进行了一定的改进。

总结

双亲委派机制是Java类加载器的核心机制,它具有以下特点:

优点:

  • 避免类的重复加载
  • 保证Java核心API的安全性
  • 维护类的唯一性

缺点:

  • 在某些场景下过于严格,需要被破坏
  • 可能导致类加载的性能问题

适用场景:

  • 大部分标准Java应用
  • 需要保证类加载安全性的场景

破坏场景:

  • 自定义类加载器
  • 热替换和热部署
  • 模块化系统
  • Web应用服务器
http://www.lryc.cn/news/586594.html

相关文章:

  • 【springcloud】快速搭建一套分布式服务springcloudalibaba(四)
  • 【一起来学AI大模型】RAG系统流程:查询→向量化→检索→生成
  • 【AI News | 20250711】每日AI进展
  • 【TOOL】ubuntu升级cmake版本
  • AI产品经理面试宝典第12天:AI产品经理的思维与转型路径面试题与答法
  • 功耗校准数据PowerProfile测试方法建议
  • 【深度剖析】致力“四个最”的君乐宝数字化转型(下篇:转型成效5-打造数字化生存能力探索可持续发展路径)
  • VUE3 el-table 主子表 显示
  • Transformer基础
  • Openpyxl:Python操作Excel的利器
  • Qt 多线程编程:单例任务队列的设计与实现
  • 五、深度学习——CNN
  • NW728NW733美光固态闪存NW745NW746
  • C语言32个关键字
  • 锁相环初探
  • Python Day11
  • 《Spring 中上下文传递的那些事儿》Part 11:上下文传递最佳实践总结与架构演进方向
  • LeetCode题解---<485.最大连续1的个数>
  • [Token]Token merging for Vision Generation
  • 【嘉立创】四层板设计
  • 当大模型遇见毫米波:用Wi-Fi信号做“透视”的室内语义SLAM实践——从CSI到神经辐射场的端到端开源方案
  • 2025年亚太杯(中文赛项)数学建模B题【疾病的预测与大数据分析】原创论文分享
  • UnityShader——SSAO
  • Matplotlib 模块入门
  • BERT:双向Transformer革命 | 重塑自然语言理解的预训练范式
  • 从 Spring 源码到项目实战:设计模式落地经验与最佳实践
  • RMSNorm实现
  • 【离线数仓项目】——数据模型开发实战
  • Druid 连接池使用详解
  • 未来软件开发的新方向:从工程到智能的深度演进