从源码探究双亲委派机制

从源码探究双亲委派机制首先我们要知道双亲委派机制是为了解决什么问题 有关类加载器 可以参考我的这篇博客 所谓的双亲委派 就是先让父亲加载器试图加载该 Class 只有在父亲加载器无法加载该类时才尝试从自己的类路径中加载该类 通俗的讲 就是某个特定的类加载器在接到加载类的请求时 首先将加载任务委托给父亲加载器 依次递归 如果父亲加载器可以完成类加载任务 就成功返回 只有父亲加载器无法完成此加载任务时 才自己去加载 我们通过这张图来理解一下 在被动的情况下 当一个类加载器收到加载请求 他不会首先自己去加载 而是传递

首先我们要知道双亲委派机制是为了解决什么问题?

有关类加载器,可以参考我的这篇博客:

双亲委派

  所谓的双亲委派,就是先让父亲加载器试图加载该Class,只有在父亲加载器无法加载该类时才尝试从自己的类路径中加载该类。
  通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父亲加载器,依次递归,如果父亲加载器可以完成类加载任务,就成功返回;
  只有父亲加载器无法完成此加载任务时,才自己去加载。




这样所有的类都会首先传递到最上层的Bootstrap ClassLoader,只有父亲加载器无法完成加载,那么此时儿子加载器才会自己去尝试加载。

 这里注意,我没有用父类加载器、子类加载器这样的语句,而是使用了父亲加载器,因为上图中这些箭头并不表示继承关系,而是一种逻辑关系, 实际上是通过组合的方式来实现的,这也是很多博客上没有写清楚,容易误导人的一点。 

通过源码来看一下双亲委派具体是怎么实现的

缓存机制
缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中。
这就是为很么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。




parent变量代表了当前class loader 的父亲加载器,这里就体现了不是通过继承,而是通过组合的方式实现类加载器之间的父子关系

如果parent等于null,那这里要说一下,当parent等于null 代表parent 为bootstrap classloader 。我们开头也讲过,bootstrap classloader是由jvm内部实现的,没有办法被程序引用,所以这里约定为null。当parent为null,就调用findBootstrapClassOrNull这个方法,让BootstrapClassLoader尝试进行加载,如果parent不为null 就让parent根据类的全限定名尝试加载,并返回该类。如果返回的class为null 说明parent加载不了,使用当前的loader的findclass方法尝试加载这个全限定名的class,需要类加载器自己去实现。

打破双亲委派机制

在大部分情况下,双亲委派机制是能够生效并且是能按预期执行的。

1第一次被破坏

本图取自咕泡学院,如有侵权,联系速删 

除非是有特殊的业务场景,一般来说不要主动去破坏双亲委派模型

那有的人可能会有疑问啦,既然jvm推荐并希望开发者遵循双亲尾派模型,那么为什么不把load class方法像defineClass设定成final来修饰?

java.lang.ClassLoader 的load class方法呢在java很早的版本就有了,而双亲委派模型是在jdk1.2引入的特性。

java是向下兼容的,也就是说引入双擎委派机制时呢,世界上已经存在了很多像我上面一样的代码,那么jvm只能向下兼容的,只能提出折中的解决方式。

这个解决的措施就是在Jdk1.2后,引入了findclass方法,推荐用户去重写该方法,而不是直接重写 Load class方法,这样呢就依然能够符合双亲委派模型。

这就是史上第一次的双亲委派模型被破坏了,像很多事情(*装)只有零次和N次,双亲委派模型第二次被破坏,是由于这个模型自身的缺陷导致,双亲委派能很好的解决了各个类加载器协作时基础类型的一致性问题,但是如果有基础类型要调用用户的代码,这又该怎么办呢?

第二次被破坏

比如说JDK想要提供操作数据库的功能,那么数据库有很多种,并且随着时间的推移将会出现各种品类的数据库,想要JDK需要针对不同的数据库和具体代码都一一实现,这不现实,jdk也不知道各种品类数据里具体的实现方式,那么比较合理的方式就是JDK提供一组规范和接口,各个不同数据库厂商按照这个接口去实现自己的类库。

SPI 怎么做呢

为了解决这个困境,Java的设计团队只好引入了一个不太优雅的设计:线程上下文类加载器 (Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContext-ClassLoader()方 法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器。

有了线程上下文类加载器,程序就可以做一些不符合双亲委派模型的事情了。JNDI服务使用这个线程上下文类,加载器去加载所需的SPI服务代码,这是一种父类加载器去请求子类加载器完成类加载的行为,这种行为实际上是打通了双亲委派模型的层次结构来逆向使用类加载器,已经违背了双亲委派模型的一般性原则,但也是无可奈何的事情。

Java中涉及SPI的加载基本上都采用这种方式来完成,例如JNDI、 JDBC、JCE、JAXB和JBI等。不过,当SPI的服务提供者多于一个的时候,代码就只能根据具体提供者的类型来硬编码判断,为了消除这种极不优雅的实现方式,在JDK 6时,JDK提供了java.util.ServiceLoader类,以META-INF/services中的配置信息,辅以责任链模式,这才算是给SPI的加 载提供了一种相对合理的解决方案。

Spring Boot 当中的SPI机制也是在JDK的机制之上去完成的,实现的思路是一样的,只不过会比JDK的实现更加优雅。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/203445.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午9:47
下一篇 2026年3月19日 下午9:47


相关推荐

  • android 自定义控件 MeasureSpec.getMode

    android 自定义控件 MeasureSpec.getModeMeasureSpec 对象包含一个 size 和一个 mode 其中 mode 可以取以下三个数值之一 UNSPECIFIED 0x 未加规定的 表示没有给子 view 添加任何规定 EXACTLY 0 0x0 精确的 表示父 view 为子 view 确定精确的尺寸 fill parentAT MOST 0x

    2026年3月19日
    2
  • leetcode 链表相加_数据结构与算法链表

    leetcode 链表相加_数据结构与算法链表给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。示例 1:输入: [2,3,-2,4]输出: 6解释: 子数组 [2,3] 有最大乘积 6。示例 2:输入: [-2,0,-1]输出: 0解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。/** * Definition for singly-linked list. * struct ListNode { * int val; *

    2022年8月9日
    6
  • 树莓派容易坏吗_树莓派的使用

    树莓派容易坏吗_树莓派的使用清华下载地址下载地址下载地址xshell下载地址③树莓派插入TF卡后,等连接上手机就可查看IP地址2.远程登录①打开后选择新建②软件更新源③系统更新源④开始更新4.图形化显示界面为方便大家更直观的感受,可以下载一个图形化的显示桌面,这里推荐VNC(windows也自带了该功能,因为太卡了故不推荐,想用的可以试试:远程桌面连接)⭐有条件的可以直接连上显示屏使用①VNC的网盘连接,大家直接下载就行②打开树莓派的VNCServer(默认关闭)A.在

    2022年10月9日
    4
  • Eclipse提示:The import java.util cannot be resolved「建议收藏」

    Eclipse提示:The import java.util cannot be resolved「建议收藏」Eclipse提示:The import java.util cannot be resolved

    2022年4月24日
    37
  • Tomcat原理简述

    Tomcat原理简述Tomcat 依赖 conf server xml 这个配置文件启动 server 一个 Tomcat 实例 核心就是启动容器 Catalina Tomcat 部署 Webapp 时 依赖 context xml 和 web xml lt CATALINA HOME gt conf 目录下的 context xml 和 web xml 在部署任何 webapp 时都会启动 他们定义一些默认行为 而具体每个 webapp 的

    2026年3月17日
    2
  • 如何使用 Coze 的 HTTP 请求节点实现高效数据交互

    如何使用 Coze 的 HTTP 请求节点实现高效数据交互

    2026年3月13日
    9

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号