JAVA 双亲委派

news2024/9/24 12:21:27

双亲委派 问题?

  1. 什么是双亲委派?
  2. 为什么需要双亲委派,不委派有什么问题?
  3. "父加载器"和"子加载器"之间的关系是继承的吗?
  4. 双亲委派是怎么实现的?
  5. 我能不能主动破坏这种双亲委派机制?怎么破坏?
  6. 为什么重写loadClass方法可以破坏双亲委派,这个方法和findClass()、defineClass()区别是什么?
  7. 说一说你知道的双亲委派被破坏的例子吧
  8. 为什么TOMCAT要破坏双亲委派?
  9. 谈谈你对模块化技术的理解吧!

1.什么是双亲委派?

首先,我们知道,虚拟机在加载类的过程中需要使用类加载器进行加载,而在Java中,类加载器有很多,那么当JVM想要加载一个.class文件的时候,到底应该由哪个类加载器加载呢?
这就不得不提到"双亲委派机制"。

首先,我们需要知道的是,Java语言系统中支持以下4种类加载器:

  • Bootstrap ClassLoader 启动类加载器
  • Extention ClassLoader 标准扩展类加载器
  • Application ClassLoader 应用类加载器
  • User ClassLoader 用户自定义类加载器

这四种类加载器之间,是存在着一种层次关系的,如下图
咳咳

一般认为上一层加载器是下一层加载器的父加载器,那么,除了BootstrapClassLoader之外,所有的加载器都是有父加载器的。

那么,所谓的双亲委派机制,指的就是:当一个类加载器收到了类加载的请求的时候,他不会直接去加载指定的类,而是把这个请求委托给自己的父加载器去加载。只有父加载器无法加载这个类的时候,才会由当前这个加载器来负责类的加载。

那么,什么情况下父加载器会无法加载某一个类呢?

其实,Java中提供的这四种类型的加载器,是有各自的职责的:

  • Bootstrap ClassLoader ,主要负责加载Java核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。
  • Extention ClassLoader,主要负责加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。
  • Application ClassLoader ,主要负责加载当前应用的classpath下的所有类
  • User ClassLoader , 用户自定义的类加载器,可加载指定路径的class文件
    那么也就是说,一个用户自定义的类,如com.hollis.ClassHollis 是无论如何也不会被Bootstrap和Extention加载器加载的。

2.为什么需要双亲委派?

如上面我们提到的,因为类加载器之间有严格的层次关系,那么也就使得Java类也随之具备了层次关系。

或者说这种层次关系是优先级。

比如一个定义在java.lang包下的类,因为它被存放在rt.jar之中,所以在被加载过程汇总,会被一直委托到Bootstrap ClassLoader,最终由Bootstrap ClassLoader所加载。

而一个用户自定义的com.hollis.ClassHollis类,他也会被一直委托到Bootstrap ClassLoader,但是因为Bootstrap ClassLoader不负责加载该类,那么会在由Extention ClassLoader尝试加载,而Extention ClassLoader也不负责这个类的加载,最终才会被Application ClassLoader加载。

这种机制有几个好处。

首先,通过委派的方式,可以避免类的重复加载,当父加载器已经加载过某一个类时,子加载器就不会再重新加载这个类。

另外,通过双亲委派的方式,还保证了安全性。因为Bootstrap ClassLoader在加载的时候,只会加载JAVA_HOME中的jar包里面的类,如java.lang.Integer,那么这个类是不会被随意替换的,除非有人跑到你的机器上, 破坏你的JDK。

那么,就可以避免有人自定义一个有破坏功能的java.lang.Integer被加载。这样可以有效的防止核心Java API被篡改。

3."父子加载器"之间的关系是继承吗?

多人看到父加载器、子加载器这样的名字,就会认为Java中的类加载器之间存在着继承关系。

甚至网上很多文章也会有类似的错误观点。

这里需要明确一下,双亲委派模型中,类加载器之间的父子关系一般不会以继承(Inheritance)的关系来实现,而是都使用组合(Composition)关系来复用父加载器的代码的。

如下为ClassLoader中父加载器的定义:

public abstract class ClassLoader { 
		// The parent class loader for delegation private final ClassLoader parent;
}

4.双亲委派是怎么实现的?

双亲委派模型对于保证Java程序的稳定运作很重要,但它的实现并不复杂。
实现双亲委派的代码都集中在java.lang.ClassLoader的loadClass()方法之中:

    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            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 thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    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、先检查类是否已经被加载过
2、若没有加载则调用父加载器的loadClass()方法进行加载
3、若父加载器为空则默认使用启动类加载器作为父加载器。
4、如果父类加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。

5.如何主动破坏双亲委派机制?

知道了双亲委派模型的实现,那么想要破坏双亲委派机制就很简单了。
因为他的双亲委派过程都是在loadClass方法中实现的,那么想要破坏这种机制,那么就自定义一个类加载器,重写其中的loadClass方法,使其不进行双亲委派即可。

6.loadClass()、findClass()、defineClass()区别

ClassLoader中和类加载有关的方法有很多,前面提到了loadClass,除此之外,还有findClass和defineClass等,那么这几个方法有什么区别呢?

  • loadClass() 就是主要进行类加载的方法,默认的双亲委派机制就实现在这个方法中。
  • findClass() 根据名称或位置加载.class字节码
  • definclass() 把字节码转化为Class这里面需要展开讲一下loadClass和findClass,我们前面说过,当我们想要自定义一个类加载器的时候,并且像破坏双亲委派原则时,我们会重写loadClass方法。

那么,如果我们想定义一个类加载器,但是不想破坏双亲委派模型的时候呢?

这时候,就可以继承ClassLoader,并且重写findClass方法。findClass()方法是JDK1.2之后的ClassLoader新添加的一个方法。

/** * @since 1.2 */protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name);}这个方法只抛出了一个异常,没有默认实现。

JDK1.2之后已不再提倡用户直接覆盖loadClass()方法,而是建议把自己的类加载逻辑实现到findClass()方法中。

因为在loadClass()方法的逻辑里,如果父类加载器加载失败,则会调用自己的findClass()方法来完成加载。

所以,如果你想定义一个自己的类加载器,并且要遵守双亲委派模型,那么可以继承ClassLoader,并且在findClass中实现你自己的加载逻辑即可。

7. 双亲委派被破坏的例子

双亲委派机制的破坏不是什么稀奇的事情,很多框架、容器等都会破坏这种机制来实现某些功能。

  • 第一种被破坏的情况是在双亲委派出现之前。
    由于双亲委派模型是在JDK1.2之后才被引入的,而在这之前已经有用户自定义类加载器在用了。所以,这些是没有遵守双亲委派原则的。
  • 第二种,是JNDI、JDBC等需要加载SPI接口实现类的情况。
  • 第三种是为了实现热插拔热部署工具。为了让代码动态生效而无需重启,实现方式时把模块连同类加载器一起换掉就实现了代码的热替换。
  • 第四种时tomcat等web容器的出现。
  • 第五种时OSGI、Jigsaw等模块化技术的应用。

为什么JNDI,JDBC等需要破坏双亲委派?

我们日常开发中,大多数时候会通过API的方式调用Java提供的那些基础类,这些基础类时被Bootstrap加载的。

但是,调用方式除了API之外,还有一种SPI的方式。

如典型的JDBC服务,我们通常通过以下方式创建数据库连接:

Connection conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/mysql”, “root”, “1234”);在以上代码执行之前,DriverManager会先被类加载器加载,因为java.sql.DriverManager类是位于rt.jar下面的 ,所以他会被根加载器加载。

类加载时,会执行该类的静态方法。其中有一段关键的代码是:

ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);这段代码,会尝试加载classpath下面的所有实现了Driver接口的实现类。

那么,问题就来了。

DriverManager是被根加载器加载的,那么在加载时遇到以上代码,会尝试加载所有Driver的实现类,但是这些实现类基本都是第三方提供的,根据双亲委派原则,第三方的类不能被根加载器加载。

那么,怎么解决这个问题呢?

于是,就在JDBC中通过引入ThreadContextClassLoader(线程上下文加载器,默认情况下是AppClassLoader)的方式破坏了双亲委派原则。

我们深入到ServiceLoader.load方法就可以看到:

public static ServiceLoader load(Class service) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl);}
第一行,获取当前线程的线程上下类加载器 AppClassLoader,于加载 classpath 中的具体实现类。

8. 为什么TOMCAT要破坏双亲委派?

Tomcat是web容器,那么一个web容器可能需要部署多个应用程序。

不同的应用程序可能会依赖同一个第三方类库的不同版本,但是不同版本的类库中某一个类的全路径名可能是一样的。

如多个应用都要依赖hollis.jar,但是A应用需要依赖1.0.0版本,但是B应用需要依赖1.0.1版本。这两个版本中都有一个类是com.hollis.Test.class。

如果采用默认的双亲委派类加载机制,那么是无法加载多个相同的类。

所以,Tomcat破坏双亲委派原则,提供隔离的机制,为每个web容器单独提供一个WebAppClassLoader加载器。

Tomcat的类加载机制:为了实现隔离性,优先加载 Web 应用自己定义的类,所以没有遵照双亲委派的约定,每一个应用自己的类加载器——WebAppClassLoader负责加载本身的目录下的class文件,加载不到时再交给CommonClassLoader加载,这和双亲委派刚好相反。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

nginx平滑升级

1.平滑升级操作1.1 备份安装目录下的nginxcd /usr/local/nginx/sbin mv nginx nginx.bak1.2 复制objs目录下的nginx到当前sbin目录下cp /opt/software/nginx/nginx-1.20.2/objs/nginx /usr/local/nginx/sbin/1.3 发送信号user2给nginx老版本对应的进程kill -user2 more /usr/lo…

【TypeScript】TypeScript的基础类型(string,number,boolean,void,null,undefined):

文章目录一、安装【1】安装npm install typescript -g【2】基础类型&#xff1a;Boolean、Number、String、null、undefined 以及 ES6 的 Symbol 和 ES10 的 BigInt二、字符串类型(string)三、数字类型(number)四、布尔类型(boolean)五、空值类型(void)六、null和undefined类型…

初探Mysql反向读取文件

前言 Mysql反向读取文件感觉蛮有意思的&#xff0c;进行了解过后&#xff0c;简单总结如下&#xff0c;希望能对在学习Mysql反向读取文件的师傅有些许帮助。 前置知识 在Mysql中存在这样一条语句 LOAD DATA INFILE它的作用是读取某个文件中的内容并放置到要求的表中&#x…

IOS崩溃文件符号化实践

1.背景与项目难点 1.1 背景 由于公司之前使用的友盟要收费&#xff0c;filebase服务由谷歌提供&#xff0c;存在数据合规风险。需要实现稳定性分析功能&#xff0c;通过支持app崩溃信息实时采集、实时上报、实时自动解析并定位出代码问题&#xff0c;帮助研发同学及时定位崩溃…

Java 布隆过滤器

你在么&#xff1f;在&#xff01;一定在么&#xff1f;不在&#xff01;一定不在么&#xff1f; 你想要100%的准去性&#xff0c;还是99%的准确性附带较高的速度和较小的资源消耗。 任何算法&#xff0c;任何经营收到的背后&#xff0c;都是时间效益 资源消耗 准确性的平衡&am…

ASO优化之如何更新APP

ASO是一个持续的迭代过程&#xff0c;应用商店排名和热门关键词每天都在变化&#xff0c;为了跟上应用行业快节奏的性质&#xff0c;我们必须灵活地制定应用商店的优化策略&#xff0c;并时常的更新我们的应用。 那我们该如何做到这一点呢&#xff1f; 如果是刚上新的应用&am…

93.【Vue-细刷-02】

Vue-02(十六)、基本列表渲染 (v-for)1.使用v-for遍历数组2.使用v-for遍历对象3.使用v-for遍历字符串(十七)、列表过滤 (filter())1.⭐JS中Change属性的原生状态⭐2.使用watch监听实现3.const {xxx} this 在Vue的作用⭐⭐4.JS箭头函数参数的简写⭐5.使用computed进行计算实现(最…

15_FreeRtos计数信号量优先级翻转互斥信号量

目录 计数型信号量 计数型信号量相关API函数 计数型信号量实验源码 优先级翻转简介 优先级翻转实验源码 互斥信号量 互斥信号量相关API函数 互斥信号量实验源码 计数型信号量 计数型信号量相当于队列长度大于1的队列&#xff0c;因此计数型信号量能够容纳多个资源,这在…

家庭理财,轻松记账修改收支记录这样操作

我们在记账的时候难免会出现记错或者想修改的地方&#xff0c;又或者是想将之前太久没有用的记账记录删除掉&#xff0c;今天&#xff0c;小编就教大家如何修改收支记录&#xff0c;一起接着往下看吧&#xff01; 第一步&#xff0c;运行【晨曦记账本】在软件主界面中&#xff…

分享111个HTML娱乐休闲模板,总有一款适合您

分享111个HTML娱乐休闲模板&#xff0c;总有一款适合您 111个HTML娱乐休闲模板下载链接&#xff1a;https://pan.baidu.com/s/1mqmJLctj9oQbJt6Oo8IuBA?pwdep3t 提取码&#xff1a;ep3t Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 响应式美容养生服务行业…

Yolo系列之YOLOv1 YOLOv2

一、YOLOv1 1. YOLOv1概述 YOLOv1是单阶段目标检测方法,不需要像Faster RCNN这种两阶段目标检测方法一样,需要生成先验框。Yolo算法采用一个单独的CNN模型实现end-to-end的目标检测。虽然Faster-RCNN在当时是mAP最高的算法(2015-2016年), 然而速度却很慢,相对而言,Yol…

Java智慧校园平台源码:SaaS模式智慧校园运营云平台源码

校班务管理&#xff1a;评价管理&#xff1a; 1.web端/教师端小程序编辑点评 多元化评价&#xff0c;捕捉学生闪光点全方位评价&#xff0c;自定义评价类型、 评价信息实时推送至家长、AI智能点评 班级报表一键导出&#xff0c;智能评测学生在校表现&#xff0c;老师、家长实…

算法的时间复杂度

算法在编写成可执行程序后&#xff0c;运行时需要消耗时间资源和空间&#xff08;内存&#xff09;资源&#xff0c;因此衡量一个算法的好坏&#xff0c;一般是从时间和空间两个维度来衡量的。 时间复杂度主要衡量一个算法运行的快慢&#xff0c;而空间复杂度主要衡量一个算法运…

Qt Desginer布局方法

首先将我们需要的控件拖拽到一个合适的位置&#xff0c;该例子中用到了两个label&#xff0c;两个lineEdit和两个pushButton。 然后我们需要利用弹簧来控制控件到控件之间的距离以及控件到窗体边界的距离&#xff0c;因为这里只有一组控件&#xff08;两个label&#xff0c;两个…

学板绘课程学费一般多少钱

学板绘课程学费一般多少钱&#xff1f;培训机构的费用和师资、模式有关&#xff0c;价格贵不贵要结合相同类型的机构多多对比。因为好些平台做了很多的宣传广告&#xff0c;运营成本很高&#xff0c; 终羊毛出在羊身上&#xff0c;这样的机构知名度很高&#xff0c;但是性价比不…

untiy 录制网络摄像头视频并保存到本地文件

网络摄像头使用的是海康威视的&#xff0c;关于如何使用Ump插件播放海康威视rtsp视频流&#xff0c;请参考我的这篇文章 内部有ump插件的下载链接 untiy接入 海康威视网络摄像头 录屏使用的插件是 AVPro movieCapture 4.6.3版&#xff0c; 插件和完整工程的下载链接放在本文的…

eclipse创建第一个java web项目并运行

为了能编写java web项目&#xff0c;建议安装支持javaee开发的eclipse版本。1.下载eclipse地址&#xff1a;https://www.eclipse.org/downloads/packages/release/2021-03/r2.解压后启动eclipse3.新建java web工程设置项目名称&#xff0c;指定tomcat的版本及路径4. 添加一个js…

Python快速入门系列之一:Python对象

Python对象1. 列表&#xff08;list&#xff09;2. 元组&#xff08;tuple&#xff09;3. 字典&#xff08;dict&#xff09;4. 集合&#xff08;set&#xff09;5. 字符串&#xff08;string&#xff09;6. BIF &#xff08;Built-in Function&#xff09;7. 列表、集合以及字…

小笔记:gitlab配置文件 /etc/gitlab/gitlab.rb 配置项

小笔记&#xff1a;gitlab配置文件 /etc/gitlab/gitlab.rb 配置项CSDN账号 jcLee95 &#xff1a;https://blog.csdn.net/qq_28550263?spm1001.2101.3001.5343 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/12…

0.3调试opencv源码的两种方式

调试opencv源码的两种方式 上两篇我们分别讲了如何配置opencv环境&#xff0c;以及如何编译opencv源码方便我们阅读。但我们还是无法调试我们的代码&#xff0c;无法以我们的程序作为入口来一步一步单点调试看opencv是如何执行的。 【opencv源码解析0.1】VS如何优雅的配置ope…