【Java】双亲委派机制及其打破的相关知识笔记

目录

类的加载机制

类加载器

双亲委派模型机制

为什么要有双亲委派机制

如何打破双亲委派机制


类的加载机制

类加载过程是由Java虚拟机的类加载子系统完成的,它负责将字节码加载到内存中,并进行链接和初始化操作。类加载过程是Java中非常重要的一部分,也是实现Java动态性和灵活性的基础。

有以下三个过程:
加载(Loading):
加载阶段是指查找字节码并创建一个代表这个类的Class对象的过程。
类加载器负责加载类的字节码文件(通常是.class文件)到内存中,并为之创建一个java.lang.Class对象。
加载阶段的主要任务是通过类的全限定名在文件系统或网络中定位到类的字节码文件,并读取到JVM内存中。

链接(Linking):
链接阶段又包括验证(Verification)、准备(Preparation)和解析(Resolution)三个子阶段。
验证阶段是确保被加载的类的字节码是合法、符合规范的过程,以防止恶意代码的引入。
准备阶段是为类的静态变量分配内存空间并初始化默认初始值(零值)的过程。
解析阶段是将常量池中的符号引用替换为直接引用的过程。例如将类或接口的全限定名转换为直接引用。

初始化(Initialization):
初始化阶段是对类进行初始化的过程,包括执行类构造器<clinit>()方法的过程。
当初始化一个类时,如果该类有父类,则会先初始化父类,如果父类还有父类,则继续向上初始化,直到顶层的父类为止。
在初始化阶段,程序员也可以通过静态代码块或静态变量的赋值语句来自定义类的初始化过程。

web安全领域值得一提的是:

Class.forName("类名")默认会初始化被加载类的静态属性和方法,因此可以注入恶意代码

而ClassLoader.loadClass默认不会初始化类方法

 

类加载器

在Java中,类加载器(ClassLoader)是Java虚拟机(JVM)的一个重要组成部分,负责将.class文件加载到内存中,并转换为Class对象。

从JVM的角度,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现(HotSpot虚拟机中),是虚拟机自身的一部分;另一种就是所有其他的类加载器,这些类加载器都有Java语言实现,独立于虚拟机外部,并且全部继承自java.lang.ClassLoader。

从开发者的角度,Java中的类加载器主要分为三种:

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

    • 启动类加载器是JVM的一部分,它负责加载Java核心类库(如rt.jar、charsets.jar等),并且是所有其他类加载器的父类加载器。
    • 启动类加载器是用本地代码实现的,不继承自java.lang.ClassLoader类,因此在Java中无法直接获取到启动类加载器的引用。
    • 启动类加载器是最顶层的类加载器,负责加载JVM运行时需要的基础类,通常不会被Java应用程序直接使用或扩展。
  2. 扩展类加载器(Extension ClassLoader):

    • 扩展类加载器是负责加载Java平台扩展库(如javax包下的类)的类加载器。
    • 扩展类加载器是由sun.misc.Launcher$ExtClassLoader实现的,并且是启动类加载器的子类加载器。
    • 扩展类加载器通常加载JRE/lib/ext目录下的jar包,或者由java.ext.dirs系统属性指定的目录中的类。
  3. 应用程序类加载器(Application ClassLoader):

    • 应用程序类加载器也称为系统类加载器,是负责加载应用程序classpath下的类的类加载器。
    • 应用程序类加载器是由sun.misc.Launcher$AppClassLoader实现的,是扩展类加载器的子类加载器。
    • 应用程序类加载器加载用户自定义的类,以及第三方类库等。

我们在加入自己定义的类加载器以满足特殊的需求时,要继承java.lang.ClassLoader类,而一个自定义ClassLoader创建时如果没有指定parent,它的parent默认就是AppClassLoader

可以用这张图来表示类加载器之间的关系:

他们并非典型的继承关系,而是子指派父为自己的 parent。

当学习自定义类加载器时,以下方法是非常重要的:

1.loadClass(加载指定的Java类):
 loadClass 方法是 ClassLoader 类中的一个重要方法,用于加载指定的 Java 类。
 在 loadClass 方法中,首先会调用父类加载器的 loadClass 方法尝试加载类,若父类加载器无法加载,则会调用 findClass 方法进行加载。
 一般情况下,开发人员不直接覆盖 loadClass 方法,而是覆盖 findClass 方法来实现自定义的类加载逻辑。

2.findClass(查找指定的Java类):
findClass 方法是自定义类加载器中一个重要的方法,用于在指定位置查找并加载指定的 Java 类。
在 findClass 方法中,开发人员可以根据自己的需求实现加载类的逻辑,比如从网络、数据库或其他特殊位置加载类文件。

3.findLoadedClass(查找JVM已经加载过的类):
findLoadedClass 方法用于查找 JVM 中是否已经加载过某个类。
通过调用 findLoadedClass 方法,可以检查某个类是否已经被加载,如果已经加载则返回对应的 Class 对象,否则返回 null。

4.defineClass(定义一个Java类):
defineClass 方法用于将字节数组转换为一个 Class 对象。
当自定义类加载器需要加载一个类时,通常会先读取该类的字节码文件,然后调用 defineClass 方法将字节数组转换为 Class 对象。

5.resolveClass(链接指定的Java类):
resolveClass 方法用于链接指定的 Java 类,即将类的二进制数据合并到 JVM 中。
在自定义类加载器加载类之后,一般需要调用 resolveClass 方法来确保类的正确链接,以便类在运行时能够正常使用。

双亲委派模型机制

一句话总结就是:一个类加载器拿到一个类后不会先去加载,而是先交由父加载器加载,由是递归,直到最顶层,当父加载器无法加载时才会委派给子加载器去加载,由是递归。

顺序是(App->Ext->Bootstrap)

展开来说:

  1. 当一个类加载器需要加载一个类时,首先检查该类是否已经被加载过了。如果已经被加载,直接返回已加载的Class对象。
  2. 如果该类没有被加载过,那么该类加载器会将这个任务委派给它的父类加载器进行加载。
  3. 父类加载器会按照相同的方式继续委派,直到达到最顶层的启动类加载器(Bootstrap ClassLoader)。
  4. 如果启动类加载器可以加载该类,则直接返回Class对象;否则,子类加载器会尝试自己加载这个类。
  5. 如果所有的父类加载器都无法加载该类,子类加载器会抛出ClassNotFoundException异常。

代码实现:

在java.lang.ClassLoader的loadClass()方法中,先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父加载失败,则抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。

①java.lang.ClassLoader的loadClass 方法:

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// 首先尝试从缓存中查找已经加载的类Class<?> c = findLoadedClass(name);if (c == null) {if (parent != null) {// 如果存在父类加载器,则委派给父类加载器加载c = parent.loadClass(name, false);} else {// 否则使用引导类加载器加载c = findBootstrapClassOrNull(name);}}if (c == null) {// 如果父类加载器无法加载,则调用自己的 findClass 方法来加载c = findClass(name);}if (resolve) {resolveClass(c);}return c;}
}

java.lang.ClassLoader的findClass 方法:

protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);
}

java.lang.ClassLoader 中,findClass 是一个抽象方法,具体的类加载器需要通过继承并重写这个方法来实现自己的类加载逻辑。在重写 findClass 方法时,开发人员通常会在其中实现自定义的类加载逻辑,比如从文件系统、网络或者其他地方加载类的字节码,并最终通过 defineClass 方法将字节码转换为 Class 对象。

下面以Extension ClassLoaderfindClass 方法为例:

protected Class<?> findClass(String name) throws ClassNotFoundException {String path = name.replace('.', '/') + ".class";URL url = findResource(path); // 在扩展库路径中查找类文件对应的资源if (url != null) {try (InputStream is = url.openStream()) {byte[] classBytes = readFully(is); // 从输入流中读取类文件的字节码return defineClass(name, classBytes, 0, classBytes.length);} catch (IOException e) {throw new ClassNotFoundException("Error reading class file for " + name, e);}} else {throw new ClassNotFoundException(name);}
}

为什么要有双亲委派机制

双亲委派模型的优势在于避免了类的重复加载和破坏类的命名空间。当一个类被加载时,系统会从上至下依次检查每个类加载器,确保每个类只被加载一次。这样可以避免不同的类加载器加载同一个类导致的类冲突问题。

如何打破双亲委派机制

第一种:

1.自定义一个类加载器,继承自 java.lang.ClassLoader。

2.在自定义类加载器中重写 loadClass 方法,并在该方法中实现自定义的类加载逻辑,不再委派给父类加载器。

3.在自定义类加载器中重写 findClass 方法,用于查找和加载类的字节码。

代码示例:

public class CustomClassLoader extends ClassLoader {@Overrideprotected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {// 自定义类加载逻辑,不再委派给父类加载器Class<?> clazz = findClass(name);if (resolve) {resolveClass(clazz);}return clazz;}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {// 实现自定义的类查找和加载逻辑,这里简单示例直接从文件系统加载类try {byte[] classData = loadClassData(name);return defineClass(name, classData, 0, classData.length);} catch (IOException e) {throw new ClassNotFoundException(name);}}private byte[] loadClassData(String className) throws IOException {// 从文件系统加载类的字节码// 这里省略具体的加载逻辑return Files.readAllBytes(Paths.get(className));}
}

第二种:

在 Java 中,通过 Thread 类的 setContextClassLoader 方法可以实现打破双亲委派模型

1.获取当前线程的类加载器:通常情况下,Java 中的类加载操作是由当前线程的上下文类加载器执行的。

2.设置线程上下文类加载器:通过 Thread 类的 setContextClassLoader 方法,可以为当前线程设置一个特定的上下文类加载器,从而实现自定义的类加载逻辑。

3.自定义类加载器:可以自定义一个类加载器,并在其中实现特定的类加载逻辑。
4.加载类或资源:在设置了线程上下文类加载器之后,通过当前线程的上下文类加载器来加载类或资源,从而实现特定线程范围内的自定义类加载逻辑。

代码示例:

public class CustomClassLoaderExample {public static void main(String[] args) {// 创建自定义类加载器CustomClassLoader customClassLoader = new CustomClassLoader();// 创建一个新线程Thread thread = new Thread(new Runnable() {@Overridepublic void run() {// 设置当前线程的上下文类加载器为自定义类加载器Thread.currentThread().setContextClassLoader(customClassLoader);// 在当前线程中使用上下文类加载器加载类try {Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass("com.example.MyClass");// 使用加载的类进行相应的操作} catch (ClassNotFoundException e) {e.printStackTrace();}}});// 启动新线程thread.start();}
}

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

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

相关文章

六、防御保护---防火墙内容安全篇

六、防御保护---防火墙内容安全篇 一、IAE&#xff08;Intelligent Awareness Engine&#xff09;引擎二、深度检测技术(DFI和DPI&#xff09;2.1 DPI -- 深度包检测技术2.1.1 基于“特征字”的检测技术2.1.2 基于应用网关的检测技术2.1.3 基于行为模式的检测技术 2.2 DFI -- 深…

163邮箱SMTP端口号及服务器地址详细设置?

163邮箱SMTP端口号是什么&#xff1f;163邮件SMTP设置教程&#xff1f; 除了基本的邮箱账号和密码外&#xff0c;还需要了解SMTP服务器地址和端口号&#xff0c;以及相应的设置。这些设置对于确保邮件能够顺利发送至关重要。下面&#xff0c;蜂邮EDM将详细介绍163邮箱SMTP端口…

RF自动化环境安装+自动化实例解析

RF定义&#xff1a; 通用型的 自动测试框架&#xff0c; 绝大部分的软件的的自动化系统都可以采用它。 特点&#xff1a; 测试数据文件&#xff08;Test Data&#xff09;对应一个个的测试用例。测试数据文件里面使用的功能小模块叫关键字&#xff0c;由测试库&#xff08;T…

【八股文学习日记】集合概述

【八股文学习日记】集合概述 集合概述 Java 集合&#xff0c; 也叫作容器&#xff0c;主要是由两大接口派生而来&#xff1a;一个是 Collection接口&#xff0c;主要用于存放单一元素&#xff1b;另一个是 Map 接口&#xff0c;主要用于存放键值对。对于Collection 接口&#…

重铸安卓荣光——双向滑块增强

痛点&#xff1a; 公司打算做安卓软件&#xff0c;最近在研究安卓&#xff0c;打算先绘制样式 研究发现安卓并不像前端有那么多组件库&#xff0c;甚至有些基础的组件都需要自己实现&#xff0c;记录一下自己实现的组件 成品展示 上面一条是谷歌提供的双向滑块&#xff0c;下面…

使用空闲电脑免费搭建一个私人的网盘

如果你也有一台空闲电脑&#xff0c;可以使用它来搭建一个私人的网盘。 这里使用的是飞梦云网盘&#xff1b; 服务端&#xff1a;下载 服务器文件使用hash校验进行储存&#xff0c;实现重复上传的文件秒传功能。 Fuse4Ui&#xff08;虚拟分区工具&#xff09;&#xff1a;下…

精品基于SpringBoot的体育馆场地预约赛事管理系统的设计与实现-选座

《[含文档PPT源码等]精品基于SpringBoot的体育馆管理系统的设计与实现[包运行成功]》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 软件开发环境及开发工具&#xff1a; Java——涉及技术&#xff1a; 前端使用技术&#…

雾锁王国服务器要开服务器吗?

雾锁王国要开服务器吗&#xff1f;可以使用官方服务器&#xff0c;也可以自己搭建多人联机服务器&#xff0c;更稳定不卡&#xff0c;畅玩开黑。阿腾云分享atengyun.com给大家目前阿里云和腾讯云均提供雾锁王国服务器和一键搭建程序&#xff0c;成本26元即可搭建一台自己的雾锁…

初识Maven

介绍&#xff1a; web后端开发技术ApacheMaven是一个项目管理和构建工具&#xff0c;它基于项目对象模型&#xff08;POM&#xff09;的概念&#xff0c;通过一小段描述信息来管理项目的构建。安装&#xff1a;http://maven.apache.org/ Apache软件基金会&#xff0c;成立于19…

pdf转word文档怎么转?分享4种转换方法

pdf转word文档怎么转&#xff1f;在日常工作中&#xff0c;我们经常遇到需要将PDF文件转换为Word文档的情况。无论是为了编辑、修改还是为了重新排版&#xff0c;将PDF转为Word都显得尤为重要。那么&#xff0c;PDF转Word文档怎么转呢&#xff1f;今天&#xff0c;就为大家分享…

【web】nginx+php环境搭建-关键点(简版)

一、nginx和php常用命令 命令功能Nginxphp-fpm启动systemctl start nginxsystemctl start php-fpm停止systemctl stop nginxsystemctl stop php-fpm重启systemctl restart nginxsystemctl restart php-fpm查看启动状态systemctl status nginxsystemctl status php-fpm开机自启…

分布式锁的应用与疑惑

文章目录 一、为什么需要用分布式锁二、Redis实现分布式锁三、Zookeeper实现分布式锁 一、为什么需要用分布式锁 集群下&#xff0c;普通的锁&#xff0c;无法解决问题 集群下&#xff0c;保证安全需要使用分布式锁 二、Redis实现分布式锁 Redisson内部封装的RedLock实现分…

有哪些ai写作软件值得推荐?这篇文章告诉你

目前AI写作软件成为越来越多写作爱好者和专业作者的首选工具。随着人工智能技术的迅猛发展&#xff0c;在国内&#xff0c;也涌现出了许多实用且好用的AI写作神器。在本文中&#xff0c;我将为您介绍7个国内好用的AI写作软件&#xff0c;同时介绍它们的优点和使用特点。 一、爱…

RV32/64 特权架构 - 特权模式与指令

RV32/64 特权架构 - 特权模式与指令 1 特权模式2 特权指令2.1 mret&#xff08;从机器模式返回到先前的模式&#xff09;2.2 sret&#xff08;从监管模式返回到先前的模式&#xff09;2.3 wfi&#xff08;等待中断&#xff09;2.4 sfence.vma&#xff08;内存屏障&#xff09; …

高级语言期末2011级A卷(软件学院)

1.编写函数&#xff0c;判定正整数m和n&#xff08;均至少为2&#xff09;是否满足&#xff1a;数m为数n可分解的最小质因数&#xff08;数n可分解的最小质因数为整除n的最小质数&#xff09; 提示&#xff1a;判定m为质数且m是n的最小因数 #include <stdio.h> #include…

【C语言】linux内核ipoib模块 - ipoib_ib_post_receive

一、中文注释 用于以太网接口&#xff08;InfiniBand&#xff09;上的IP over IB&#xff08;IPoIB&#xff09;设备的Linux内核函数&#xff0c;负责将接收缓冲区&#xff08;一个包&#xff09;提交到网络设备的队列中等待数据到达。下面是中文注释版本的函数代码&#xff1…

数仓模型设计方法论

在当今大数据时代&#xff0c;数据已经成为企业最重要的资产之一。而数据仓库作为企业数据管理和分析的核心基础设施&#xff0c;其设计方法论对于企业的数据治理和决策分析至关重要。本文将探索数仓模型设计的方法论&#xff0c;帮助读者更好地理解和应用数仓模型设计。 一、…

nodejs 实现pdf与图片互转

PDF转图片 效果图 代码 const path require(path); const pdf require(pdf-poppler); const fs require(fs); // PDF文件路径 const pdfFilePath ./path/test.pdf; // 转换选项 const opts { format: png, // 输出图片格式&#xff0c;可以是 jpeg, png, ppm…

华为ipv6 over ipv4 GRE隧道配置

思路&#xff1a; PC1访问PC2时&#xff0c;会先构造源ipv6为2001:1::2&#xff0c;目的IPV6为2001:2::2的ipv6报文&#xff0c;然后查看PC1的路由表&#xff0c;发送到R1&#xff0c;r1接收后&#xff0c;以目的IPV6地址2001:2::2查询IPV6路由表&#xff0c;出接口为tun0/0/0…

如何运行github上的项目

为了讲明白这个过程&#xff0c;特意做了一个相当来说比较好读懂的原理图&#xff0c;希望和我一样初学的小伙伴也能很快上手哈&#x1f60a; 在Github中找到想要部署的项目&#xff0c;这里以BartoszJarocki/CV&#xff08;线上简历&#x1f4c4;&#xff09;项目为例 先从头…