Class Loading -- 06 -- 破坏双亲委派模型

双亲委派模型在Java类加载机制中扮演关键角色,但存在三次大规模被破坏的情况。首次破坏源于JDK2.0引入findClass()方法,以保持与JDK1.0自定义类加载器的兼容。第二次破坏由JNDI服务调用SPI代码需求引发,通过引入线程上下文类加载器解决。第三次破坏则因用户追求代码热替换和模块热部署,OSGI框架通过自定义类加载器机制实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文链接:Class Loading – 06 – 破坏双亲委派模型


相关文章:


双亲委派模型并不是一个强制性的约束模型,而是 Java 设计者推荐给开发者的类加载器实现方式,在 Java 的世界中大部分的类加载器都遵循这个模型,但也有例外,到目前为止,双亲委派模型主要出现过三次较大规模的 “被破坏” 情况


一、第一次被破坏

  • 发生在双亲委派模型出现之前,双亲委派模型在 JDK2.0 之后才被引入,为了向前兼容 JDK1.0 已经存在的自定义类加载器,JDK2.0 之后的 java.lang.ClassLoader 添加了一个新的 protected 方法 findClass()

  • 在此之前,我们去继承 java.lang.ClassLoader 的唯一目的就是为了重写 loadClass() 方法,因为虚拟机在进行类加载的时候会调用加载器的私有方法 loadClassInternal(),而这个方法的唯一逻辑就是去调用自己的 loadClass() 方法

  • JDK2.0 之后已经不再提倡去覆盖 loadClass() 方法,而应当把自己的类加载逻辑写到 findClass() 方法当中,在 loadClass() 方法的逻辑里,如果父类加载器加载失败,则会调用自己的 findClass() 方法来完成加载,这样就可以保证自定义的类加载器是遵循双亲委派模型的


二、第二次被破坏

  • 由双亲委派模型自身的缺陷所导致,双亲委派很好地解决了各个类加载器基础类的统一问题 (越基础的类由越上层的加载器进行加载),如果基础类又要回调用户的代码,就会出现问题

  • 一个典型的例子便是 JNDI (Java Naming and Directory Interface:Java 命名与目录接口) 服务,它的代码由启动类加载器去加载 (在 JDK3.0 时放入 rt.jar 中),但 JNDI 的目的就是对资源进行集中管理和查找,它需要调用由独立厂商实现并部署在应用程序 CLASSPATH 下的 JNDI 接口提供者 (SPI:Service Provider Interface) 的代码,但启动类无法加载这些代码

  • 为了解决这个问题,引入了线程上下文类加载器 (Thread Context ClassLoader),可以通过 java.lang.Thread 类的 setContextClassLoaser() 方法进行设置

    • 如果创建线程时还未设置,它将会从父线程中继承一个

    • 如果在应用程序的全局范围内都没有设置过的话,那么这个类加载器默认就是应用程序类加载器

  • JNDI 服务可以使用这个线程上下文类加载器去加载所需要的 SPI 代码,也就是父类加载器委派子类加载器去完成类的加载动作,这种行为实际上逆向使用了类加载器,已经违背了双亲委派模型的一般性原则


三、第三次被破坏

  • 由用户对程序动态性的追求所导致,这里的动态性指的是:代码热替换 (HotSwap)、模块热部署 (Hot Deployment)

  • 提到动态性,那就不能不提到 OSGI (Open Service Gateway Initiative:面向 Java 的动态模型系统),目前 OSGI 已经成为业界 “事实上” 的 Java 模块化标准,OSGI 实现模块化热部署的关键在于其自定义的类加载器机制的实现

  • 每一个程序模块 (OSGI 中称为 Bundle) 都有一个自己的类加载器,当需要更换一个 Bundle 时,就把 Bundle 连同类加载器一起换掉以实现代码的热替换

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值