秒懂双亲委派机制

前言

最近知识星球中,有位小伙伴问了我一个问题:JDBC为什么会破坏双亲委派机制?

这个问题挺有代表性的。

双亲委派机制是Java中非常重要的类加载机制,它保证了类加载的完整性和安全性,避免了类的重复加载。

这篇文章就跟大家一起聊聊,Java中类加载的双亲委派机制到底是怎么回事,有哪些破坏双亲委派机制的案例,为什么要破坏双亲委派机制,希望对你会有所帮助。

1 为什么要双亲委派机制?

我们的Java在运行之前,首先需要把Java代码转换成字节码,即class文件。

然后JVM需要把字节码通过一定的方式加载到内存中的运行时数据区

这种方式就是类加载器(ClassLoader)。

再通过加载、验证、准备、解析、初始化这几个步骤完成类加载过程,然后再由jvm执行引擎的解释器和JIT即时编译器去将字节码指令转换为本地机器指令进行执行。

我们在使用类加载器加载类的时候,会面临下面几个问题:

  1. 如何保证类不会被重复加载?类重复加载会出现很多问题。

  2. 类加载器是否允许用户自定义?

  3. 如果允许用户自定义,如何保证类文件的安全性?

  4. 如何保证加载的类的完整性?

为了解决上面的这一系列的问题,我们必须要引入某一套机制,这套机制就是:双亲委派机制

2 什么是双亲委派机制?

接下来,我们看看什么是双亲委派机制。

双亲委派机制的基本思想是:当一个类加载器试图加载某个类时,它会先委托给其父类加载器,如果父类加载器无法加载,再由当前类加载器自己进行加载。

这种层层委派的方式有助于保障类的唯一性,避免类的重复加载,并提高系统的安全性和稳定性。

在Java中默认的类加载器有3层:

  1. 启动类加载器(Bootstrap Class Loader):负责加载 %JAVA_HOME%/jre/lib 目录下的核心Java类库,比如:rt.jar、charsets.jar等。它是最顶层的类加载器,通常由C++编写。

  2. 扩展类加载器(Extension Class Loader):负责加载Java的扩展库,一般位于<JAVA_HOME>/lib/ext目录下。

  3. 应用程序类加载器(Application Class Loader):也称为系统类加载器,负责加载用户类路径(ClassPath)下的应用程序类。

用一张图梳理一下,双亲委派机制中的3种类加载器的层次关系:

图片

但这样不够灵活,用户没法控制,加载自己想要的一些类。

于是,Java中引入了自定义类加载器。

创建一个新的类并继承ClassLoader类,然后重写findClass方法。

该方法主要是实现从那个路径读取 ar包或者.class文件,将读取到的文件用字节数组来存储,然后可以使用父类的defineClass来转换成字节码。

如果想破坏双亲委派的话,就重写loadClass方法,否则不用重写。

类加载器的层次关系改成:

图片

双亲委派机制流程图如下:

图片

具体流程大概是这样的:

  1. 需要加载某个类时,先检查自定义类加载器是否加载过,如果已经加载过,则直接返回。

  2. 如果自定义类加载器没有加载过,则检查应用程序类加载器是否加载过,如果已经加载过,则直接返回。

  3. 如果应用程序类加载器没有加载过,则检查扩展类加载器是否加载过,如果已经加载过,则直接返回。

  4. 如果扩展类加载器没有加载过,则检查启动类加载器是否加载过,如果已经加载过,则直接返回。

  5. 如果启动类加载器没有加载过,则判断当前类加载器能否加载这个类,如果能加载,则加载该类,然后返回。

  6. 如果启动类加载器不能加载该类,则交给扩展类加载器。扩展类加载器判断能否加载这个类,如果能加载,则加载该类,然后返回。

  7. 如果扩展类加载器不能加载该类,则交给应用程序类加载器。应用程序类加载器判断能否加载这个类,如果能加载,则加载该类,然后返回。

  8. 如果应用程序类加载器不能加载该类,则交给自定义类加载器。自定义类加载器判断能否加载这个类,如果能加载,则加载该类,然后返回。

  9. 如果自定义类加载器,也无法加载这个类,则直接抛ClassNotFoundException异常。

这样做的好处是:

  1. 保证类不会重复加载。加载类的过程中,会向上问一下是否加载过,如果已经加载了,则不会再加载,这样可以保证一个类只会被加载一次。

  2. 保证类的安全性。核心的类已经被启动类加载器加载了,后面即使有人篡改了该类,也不会再加载了,防止了一些有危害的代码的植入。

3 破坏双亲委派机制的场景

既然Java中引入了双亲委派机制,为什么要破坏它呢?

答:因为它有一些缺点。

下面给大家列举一下,破坏双亲委派机制最常见的场景。

3.1 JNDI

JNDI是Java中的标准服务,它的代码由启动类加载器去加载。

但JNDI要对资源进行集中管理和查找,它需要调用由独立厂商在应用程序的ClassPath下的实现了JNDI接口的代码,但启动类加载器不可能“认识”这些外部代码。

为了解决这个问题,Java后来引入了线程上下文类加载器(Thread Context ClassLoader)。

这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置。

如果创建线程时没有设置,他将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器。

有了线程上下文加载器,JNDI服务就可以使用它去加载所需要的SPI代码,也就是父类加载器请求子类加载器去完成类加载的动作,这样就打破了双亲委派机制。

3.2 JDBC

原生的JDBC中Driver驱动本身只是一个接口,并没有具体的实现,具体的实现是由不同数据库类型去实现的。

例如,MySQL的mysql-connector.jar中的Driver类具体实现的。

原生的JDBC中的类是放在rt.jar包,是由启动类加载器进行类加载的。

在JDBC中需要动态去加载不同数据库类型的Driver实现类,而mysql-connector.jar中的Driver实现类是用户自己写的代码,启动类加载器肯定是不能加载的,那就需要由应用程序启动类去进行类加载。

为了解决这个问题,也可以使用线程上下文类加载器(Thread Context ClassLoader)。

3.3  Tomcat容器

Tomcat是Servlet容器,它负责加载Servlet相关的jar包。

此外,Tomcat本身也是Java程序,也需要加载自身的类和一些依赖jar包。

这样就会带来下面的问题:

  1. 一个Tomcat容器下面,可以部署多个基于Servlet的Web应用,但如果这些Web应用下有同名的Servlet类,又不能产生冲突,需要相互独立加载和运行才行。

  2. 但如果多个Web应用,使用了相同的依赖,比如:SpringBoot、Mybatis等。这些依赖包所涉及的文件非常多,如果全部都独立,可能会导致JVM内存不足。也就是说,有些公共的依赖包,最好能够只加载一次。

  3. 我们还需要将Tomcat本身的类,跟Web应用的类隔离开。

这些原因导致,Tomcat没有办法使用传统的双亲委派机制加载类了。

那么,Tomcat加载类的机制是怎么样的?

图片

  • CommonClassLoader:是Tomcat最基本的类加载器,它加载的类可以被Tomcat容器和Web应用访问。

  • CatalinaClassLoader:是Tomcat容器私有的类加载器,加载类对于Web应用不可见。

  • SharedClassLoader:各个Web应用共享的类加载器,加载的类对于所有Web应用可见,但是对于Tomcat容器不可见。

  • WebAppClassLoader:各个Web应用私有的类加载器,加载类只对当前Web应用可见。比如不同war包应用引入了不同的Spring版本,这样能加载各自的Spring版本,相互隔离。

3.4 热部署

由于用户对程序动态性的追求,比如:代码热部署、代码热替换等功能,引入了OSGi(Open Service Gateway Initiative)。

OSGi中的每一个模块(称为Bundle)。

当程序升级或者更新时,可以只停用、重新安装然后启动程序的其中一部分,对企业来说这是一个非常诱人的功能。

OSGi的Bundle类加载器之间只有规则,没有固定的委派关系。

各个Bundle加载器是平级关系。

不是双亲委派关系。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/738872.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

北斗三号短报文通信终端 | 助力户外无网络场景作业

北斗三号短报文通信终端是一款专为户外无网络场景作业设计的先进通信工具&#xff0c;它依托于中国自主研发的北斗卫星导航系统&#xff0c;为用户在偏远地区或无网络覆盖区域提供了可靠的通信保障。以下是关于北斗三号短报文通信终端的详细介绍&#xff1a; 一、功能特点 北斗…

[Python人工智能] 四十六.PyTorch入门 (1)环境搭建、神经网络普及和Torch基础知识

从本专栏开始,作者正式研究Python深度学习、神经网络及人工智能相关知识。前文讲解合如何利用keras和tensorflow构建基于注意力机制的CNN-BiLSTM-ATT-CRF模型,并实现中文实体识别研究。这篇文章将介绍PyTorch入门知识。前面我们的Python人工智能主要以TensorFlow和Keras为主,…

JavaWeb系列十六: jQuery初步入门

跟老韩-JavaScript开发利器之jQuery 1.1 原理示意图1.2 快速入门1.2 什么是jquery对象1.3 dom对象转jQuery对象1.4 jQuery对象转dom对象 jQuery是一个快速的, 简洁的javaScript库, 使用户能更方便地处理HTML, css, dom…提供方法, events, 选择器, 并且方便地为网站提供AJAX交互…

FFmpeg交叉编译报错pkg-config not found

ffmpeg交叉编译时报错&#xff1a; WARNING: arm-linux-gnueabihf-pkg-config not found, library detection may fail.不慌&#xff0c;没有就下载嘛&#xff0c;直接install&#xff1a; sudo apt-get install pkg-config-arm-linux-gnueabihf 参考&#xff1a; How To I…

无水蒸汽室的热特性​研究

更多资讯&#xff0c;请关注公众号【莱歌数字】~~ 扩散电阻在从源到汇的整体传热过程中继续起着主导作用。 随着电子元件占地面积小和高功耗的趋势&#xff0c;需要在散热器的底部散热对于降低扩散电阻变得非常重要。 在一些应用中&#xff0c;如高功率激光器&#xff0c;可…

JavaWeb系列十七: jQuery选择器 上

jQuery选择器 jQuery基本选择器jquery层次选择器基础过滤选择器内容过滤选择器可见度过滤选择器 选择器是jQuery的核心, 在jQuery中, 对事件处理, 遍历 DOM和Ajax 操作都依赖于选择器jQuery选择器的优点 $(“#id”) 等价于 document.getElementById(“id”);$(“tagName”) 等价…

Anzo Capital昂首资本独家揭秘,掌握价格行为交易法则,轻松盈利

探索交易成功的秘密!Anzo Capital昂首资本独家揭秘价格行为模式的五大核心步骤&#xff0c;助各位投资者都能把握市场脉搏&#xff0c;轻松盈利。 第一步&#xff0c;精准识别市场趋势&#xff0c;为成功交易奠定坚实基础。 第二步&#xff0c;洞察图表密码&#xff0c;巧妙标…

程序员系统入门大模型的路径和资源,看这篇就够了

本篇文章面向对大模型领域感兴趣&#xff0c;又不知如何下嘴的程序员。 看一下围绕大模型的应用场景和人才需求&#xff1a; **Prompt工程&#xff1a;**基于提示词对大模型的使用&#xff0c;会问问题就行。 **基于大模型的应用&#xff08;狭义的&#xff09;&#xff1a;*…

Avalonia 常用控件二 Menu相关

1、Menu 添加代码如下 <Button HorizontalAlignment"Center" Content"Menu/菜单"><Button.Flyout><MenuFlyout><MenuItem Header"打开"/><MenuItem Header"-"/><MenuItem Header"关闭"/&…

一文讲清楚人工智能集成学习之多模型投票(Voting)

一、集成学习 集成学习是人工智能领域中一种强大的机器学习方法&#xff0c;它通过结合多个学习器来提高整体的预测或分类性能&#xff0c;通常能够比单一模型表现得更好。 1.1 集成学习的原理 集成学习的核心思想是“集思广益”&#xff0c;即通过集合多个模型的预测结果来提…

面向对象修炼手册(二)(消息与继承)(Java宝典)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;面向对象修炼手册 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 前言 消息传递 1 基本概念 1.…

Python19 lambda表达式

在 Python 中&#xff0c;lambda 表达式是一个小型匿名函数&#xff0c;通常用于实现简单、单行的函数。lambda 函数可以接受任意数量的参数&#xff0c;但只能有一个表达式。 基本语法&#xff1a; lambda arguments: expression这里&#xff0c;arguments 是传递给 lambda …

LeetCode —— 只出现一次的数字

只出现一次的数字 I 本题依靠异或运算符的特性&#xff0c;两个相同数据异或等于0&#xff0c;数字与0异或为本身即可解答。代码如下: class Solution { public:int singleNumber(vector<int>& nums) {int ret 0;for (auto e : nums){ret ^ e;}return ret;} };只出…

Kubernetes排错(十)-处理容器数据磁盘被写满

容器数据磁盘被写满造成的危害: 不能创建 Pod (一直 ContainerCreating)不能删除 Pod (一直 Terminating)无法 exec 到容器 如何判断是否被写满&#xff1f; 容器数据目录大多会单独挂数据盘&#xff0c;路径一般是 /var/lib/docker&#xff0c;也可能是 /data/docker 或 /o…

基于CDMA的多用户水下无线光通信(3)——解相关多用户检测

继续上一篇博文&#xff0c;本文将介绍基于解相关的多用户检测算法。解相关检测器的优点是因不需要估计各个用户的接收信号幅值而具有抗远近效应的能力。常规的解相关检测器有运算量大和实时性差的缺点&#xff0c;本文针对异步CDMA的MAI主要来自干扰用户的相邻三个比特周期的特…

wordpress教程自动采集并发布工具

随着互联网的快速发展&#xff0c;越来越多的人开始关注网络赚钱。而对于许多人来说&#xff0c;拥有一个自己的个人网站是一个不错的选择。然而&#xff0c;要让自己的个人网站内容丰富多样&#xff0c;就需要不断地进行更新。那么&#xff0c;有没有一种方法可以让我们轻松地…

服务器数据恢复—raid5热备盘同步失败导致阵列崩溃如何恢复数据?

服务器存储数据恢复环境&故障&#xff1a; 某品牌DS5300存储&#xff0c;包含一个存储机头和多个磁盘柜&#xff0c;组建了多组RAID5磁盘阵列。 某个磁盘柜中的一组RAID5阵列由15块数据盘和1块热备硬盘组建。该磁盘柜中的某块硬盘离线&#xff0c;热备盘自动替换并开始同步…

C语言入门系列:从内存原理看函数的值传递和引用传递

文章目录 一&#xff0c;值传递二&#xff0c;引用传递三&#xff0c;从内存原理看值传递和引用传递的区别1 值传递内存示意图2 引用传递内存示意图 参考文献 函数参数用于向函数传递数据&#xff0c;C语言支持两种传递方式&#xff1a;值传递和引用传递。 一&#xff0c;值传递…

校史馆虚拟翻书设计有哪些技术上的亮点?

近年来&#xff0c;互动多媒体已成为展览展示行业中备受瞩目的焦点&#xff0c;尤其是那些凭借尖端的投影与感应技术打造的互动装置&#xff0c;它们在各个主题展厅中独领风骚&#xff0c;其独特魅力不言而喻。而当我们深入思考其在校史馆设计中的应用时&#xff0c;不难预见&a…

【每日一题】503. 下一个更大元素 II

思路&#xff08;c&#xff09; 使用单调栈的解法&#xff0c;单调栈里面存的是大于当前元素的index&#xff0c;不是直接存值。但是这个题目是一个循环数组&#xff0c;可以在后面补一段前面的内容&#xff0c;这样就可以正常遍历。将每个位置对应的结果存在map中 后面循环…