类的动态加载-双亲委派模型

java反射基础

Java 基础 - 反射机制详解 | Java 全栈知识体系 (pdai.tech)

类的动态加载

参考链接:类的动态加载

构造是和实例化也就是对象相关的。

静态代码块是在初始化的时候就调用的 Class.forName();就会调用静态代码块

forName,加载类时默认初始化

        Class.forName();    //默认初始化
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();Class.forName("Person",false,classLoader);//不进行初始化

类加载器的研究

类加载器,加载类时默认不初始化。

        ClassLoader classLoader = ClassLoader.getSystemClassLoader();Class<?> person = classLoader.loadClass("Person");Class<?> person = Class.forName("Person", false, classLoader); //两个代码作用相同

底层的原理,实现加载任意的类

java双亲委派

image-20240704094900997

Class<?> person1 = classLoader.loadClass("Person");处打个断点进行调试。

首先调用ClassLoader.classLoader(a)因为AppClassLoader中的classLoader参数是两个,所以调用到了其父类ClassLoader.classLoader(a,b)

之后ClassLoader.classLoader(a,b)调用AppClassLoader.classLoader(a), —> ClassLoader.classLoader(a,b)

在ClassLoader.classLoader(a,b)中就涉及到了双亲委派模型

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);             //检查类有没有被加载if (c == null) {                                //类没有被加载 进入long t0 = System.nanoTime();try {if (parent != null) {                   //还有父加载器的话,让父加载器loadClass   这里也就是ExtClassLoaderc = 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 statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}

进入ExtClassLoader.loadCLass, 因为其中没有loadCLass所以又直接调用到了,上面CLass.loadClass方法。

这次

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {              //找不到父加载器了,因为bootstrap ClassLoader 不在java中c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);  //走到这一步 也不回找到 因为是一个普通的类 不会调用BootstrapClassLoader去加载}} 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);              //之后走到findClass("Person") 因为最后是在App CLassLoader中加载的 所以ExtClassLoader中先不跟了 返回的是null// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}

之后return c=null逻辑又回到了 AppCLassLoader的loadCLass

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);             //检查类有没有被加载if (c == null) {                                //类没有被加载 进入long t0 = System.nanoTime();try {if (parent != null) {                   //还有父加载器的话,让父加载器loadClass   这里也就是ExtClassLoaderc = parent.loadClass(name, false);  //ExtCLassLoader返回null。所以c=null} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {                           //因为C=null 进入// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);                   //进到findClass("Person") 跟一下这里// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}

下面跟进findClass(“Person”)

因为AppClassLoader总没有findClass方法,所以找到了其父类URLClassLoader的findClass

protected Class<?> findClass(final String name)throws ClassNotFoundException
{final Class<?> result;try {result = AccessController.doPrivileged(new PrivilegedExceptionAction<Class<?>>() {public Class<?> run() throws ClassNotFoundException {String path = name.replace('.', '/').concat(".class");Resource res = ucp.getResource(path, false);          //ucp是类的路径 URLClassPath类if (res != null) {                                    //res不为空 进入try {return defineClass(name, res);                //主要跟一下defindClass} catch (IOException e) {throw new ClassNotFoundException(name, e);}} else {return null;}}}, acc);} catch (java.security.PrivilegedActionException pae) {throw (ClassNotFoundException) pae.getException();}if (result == null) {throw new ClassNotFoundException(name);}return result;
}

下图可以观察到 AppClassLoader调用findCLass时,ucp(查找路径)里面加入了file:/H:/java_des/target/classes/(我们项目Class路径),所以可以查到Person类,res不为空。

image-20240704135445797

跟一下URLCLassLoader.defineClass

image-20240704140107786

return 调用的是URLClassLoader的父类SecureClassLoader的defineClass方法

image-20240704140249660

return 调用的是CLassLoader的defineClass方法

protected final Class<?> defineClass(String name, byte[] b, int off, int len,ProtectionDomain protectionDomain)     //这里name是类名,b是字节码throws ClassFormatError
{protectionDomain = preDefineClass(name, protectionDomain);String source = defineClassSourceLocation(protectionDomain);Class<?> c = defineClass1(name, b, off, len, protectionDomain, source); //在defineClass1完成类的加载(字节码) 是个native类postDefineClass(c, protectionDomain);return c;
}

之后一层一层返回加载的类,加载到了URLCLass.findClass中return defineClass(name, res);处。

下一步,也就是最终返回到了我们写的loadClass方法调用代码处,可以看到返回了Person类

在这里插入图片描述

总结

ClassLoader -> SecureClassLoader -> URLCLassLoader -> AppClassLoader (继承关系 父->子)

ClassLoader.loadClass -> URLCLass.findClass(重写方法)(路径中能找到类才进入defineCLass) ->SecureClassLoader.defineClass(从字节码加载类)->ClassLoader.defineClass

image-20240704144522395

利用

先编写并编译一个弹计算器的代码,放到一个指定路径,之后把项目中的Test.class删除(项目路径没有Test.class,看看是否能够通过类加载器的利用,找到类)

import java.io.IOException;public class Test {static  {  //静态代码块try {Runtime.getRuntime().exec("calc");} catch (IOException e) {e.printStackTrace();}}
}

URLCLassLoader 任意类加载:file/http/jar 协议

public class LoadClassTest {public static void main(String[] args) throws Exception {URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///G:\\Java反序列化\\class_test\\")}); //指定Class的查找路径URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://127.0.0.1:9999/")}); //指定Class的查找路径URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("jar:http://127.0.0.1:9999/Test.jar!/")}); //指定Class的查找路径Class<?> c = urlClassLoader.loadClass("Test"); //load Person类c.newInstance();  //实例化}
}

ClassLoader.defineClass 字节码加载任意类 私有

public class LoadClassTest {public static void main(String[] args) throws Exception {ClassLoader classLoader = ClassLoader.getSystemClassLoader();Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);defineClass.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("G:\\Java反序列化\\class_test\\Test.class"));Class c = (Class) defineClass.invoke(classLoader, "Test",code, 0, code.length);  //对象 类名 字节码 字节码起始 字节码长度 defineClass返回的是Class<?> 这里也就是返回的Test.classc.newInstance(); //实例化 触发静态代码块}
}

Unsafe.defineClass 字节码加载 public类但是不能直接调用,需要先反射调用public方法实例化类

Unsafe类中defineClass方法是public的,但是是个单例模式,不能直接调用defineClass()。

看到有个getUnsafe方法,是public的,但是直接调用Unsafe.getUnsafe()是会报错的因为有个安全检查。

最后找到theUnsafe属性

private static final Unsafe theUnsafe = new Unsafe();

所以反射调用theUnsafe属性去实例化Unsafe

public native Class<?> defineClass(String name, byte[] b, int off, int len,ClassLoader loader,ProtectionDomain protectionDomain);
public class LoadClassTest {public static void main(String[] args) throws Exception {ClassLoader classLoader = ClassLoader.getSystemClassLoader();byte[] code = Files.readAllBytes(Paths.get("G:\\Java反序列化\\class_test\\Test.class"));Class unsafe = Unsafe.class;Field theUnsafeField = unsafe.getDeclaredField("theUnsafe");theUnsafeField.setAccessible(true);Unsafe unsafe1 = (Unsafe) theUnsafeField.get(null);Class<?> test = unsafe1.defineClass("Test", code, 0, code.length, classLoader, null);test.newInstance();}
}

Spring里面可以直接生成

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

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

相关文章

你们叫AI,我们叫DI

大家好&#xff0c;才是真的好。 最近Notes/Domino产品在做哪些更新&#xff0c;想必大家都很好奇。 从2022年年末到现在&#xff0c;快两年了&#xff0c;任何一个有追求的大企业或巨头&#xff0c;应该都在追求实现一件事情&#xff1a;AI人工智能。 从小道消息来看&#…

【高级篇】分区与分片:MySQL的高级数据管理技术(十三)

引言 在上一章,我们探讨了MySQL的主从复制与高可用性,这是构建健壮数据库架构的基石。现在,让我们深入到更高级的主题——分区与分片,这些技术对于处理大规模数据集和提升数据库性能至关重要。我们将详细介绍表分区的概念、类型及分片技术的应用,为下一章讨论MySQL集群与…

解决Vue3中路由页面跳转出现白屏,刷新页面之后展示正常的问题

遇到这个问题&#xff0c;首先需要检查根组件标签最外层是否包含了个最大的div盒子来包裹内容。如下图所示&#xff1a; 我的项目就是因为没有将两块内容放到一个大盒子里面&#xff0c;所以才会出现白屏的问题。然后我去查了相关的资料&#xff0c;了解到这个问题是Vue组件渲染…

TSINGSEE智能分析网关V4人员区域徘徊AI检测:算法原理介绍及技术应用场景

一、引言 在现代社会&#xff0c;随着科技的不断发展&#xff0c;视频监控系统已广泛应用于各个领域&#xff0c;如公共安全、商业管理、交通监控等。其中&#xff0c;区域徘徊检测算法作为一种重要的视频分析技术&#xff0c;能够有效地识别出特定区域内人员的徘徊行为&#…

Spring Cloud Alibaba - Sentinel 分布式系统流量哨兵

目录 概述特征基本概念 安装Sentinel微服务引入Sentinel案例流控规则&#xff08;流量控制&#xff09;流控模式-直接流控模式-关联流控模式-链路流控效果-快速失败流控效果-预热WarmUp流控效果-排队等候 流控规则&#xff08;并发线程数控制&#xff09;熔断规则&#xff08;熔…

Django 安装 Zinnia 后出现故障

在Django中安装和配置Zinnia时遇到故障可能有多种原因&#xff0c;通常包括版本兼容性、依赖关系或配置问题。这里提供一些常见的解决方法和调试步骤&#xff0c;帮助大家解决问题。 首先&#xff0c;确保您安装的Zinnia版本与Django版本兼容。查看Zinnia的官方文档或GitHub页…

Linux库概念及相关编程(动态库-静态库)

Linux库概念及相关编程 分文件编程案例 分文件编程是指将程序按功能模块划分成不同的文件进行编写&#xff0c;这种方法有以下好处&#xff1a; 功能责任划分&#xff1a;每个文件对应一个功能模块&#xff0c;职责明确&#xff0c;易于理解和维护。方便调试&#xff1a;可以…

C++左值/右值/左值引用/右值引用

1&#xff09;C入门级小知识&#xff0c;分享给将要学习或者正在学习C开发的同学。 2&#xff09;内容属于原创&#xff0c;若转载&#xff0c;请说明出处。 3&#xff09;提供相关问题有偿答疑和支持。 左值和右值的概念&#xff1a; 早期的c语言中关于左值和右值的定义&a…

每日一题——Python实现PAT乙级1026 程序运行时间(举一反三+思想解读+逐步优化)五千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 代码结构和逻辑 时间复杂度 空间复杂度 代码优化建议 总结 我要更强 …

交换机需要多大 buffer

有点违背直觉&#xff0c;但是真事儿&#xff0c;交换机过境的流越多&#xff0c;所需 buffer 越小&#xff0c;这是为什么&#xff1f; 范氏(范雅各布森&#xff0c;van jacobson)管道的 aimd 流建议 buffer_size 为 bdp&#xff0c;这很容易理解&#xff0c;因为 aimd 流最小…

币界网讯,预计以太坊现货 ETF 将于 7 月中旬推出

刚刚 ETF Store 总裁 Nate Geraci 在 X &#xff08;前Twitter&#xff09;平台上宣布&#xff0c;备受数字货币市场期待的SEC以太坊现货 ETF提案&#xff0c;将于7 月中旬通过美国证券交易委员会&#xff08;SEC&#xff09;批准。Nate Geraci透露修订后的 S-1 文件将于 7 月 …

pnpm的坑

请问pnpm的两个坑怎么解决&#xff1a; 第一个坑&#xff1a;没有节省磁盘空间 我已经配置了依赖的存储位置&#xff0c; 但我在项目里pnpm install以后&#xff0c;发现依赖包还是很大&#xff0c; 然后发现里面的链接并不是指向先前配置的依赖存储位置&#xff0c;而是指…

【数智化人物展】袋鼠云CEO宁海元:大模型时代,Data+AI将成为新的基础设施

宁海元 本文由袋鼠云CEO宁海元投递并参与由数据猿联合上海大数据联盟共同推出的《2024中国数智化转型升级先锋人物》榜单/奖项评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 身处这个瞬息万变的数字经济时代&#xff0c;传统的生产模式往往依赖于经验和固定的流程&…

k8s-第六节-数据持久化

数据持久化 kubernetes 集群不会为你处理数据的存储&#xff0c;需要为数据库挂载一个磁盘来确保数据的安全。 可以选择云存储、本地磁盘、NFS。 本地磁盘&#xff1a;可以挂载某个节点上的目录&#xff0c;但是这需要限定 pod 在这个节点上运行 云存储&#xff1a;不限定节…

GEE计算遥感生态指数RESI

目录 RESI湿度绿度热度干度源代码归一化函数代码解释整体的代码功能解释:导出RSEI计算结果参考文献RESI RSEI = f (Greenness,Wetness,Heat,Dryness)其遥感定义为: RSEI = f (VI,Wet,LST,SI)式中:Greenness 为绿度;Wetness 为湿度;Thermal为热度;Dryness 为干度;VI 为植被指数…

手写starter核心思路流程-全网最详细版本

全网最详细手写starter组件教程 那么在写这篇博客之前,先问一下大家为什么要写starter组件,仅仅只是为了炫技吗?还是真正的在业务中需要.在现在的开发环境下,什么是竞争力? 举例分页查询来说,每个来公司的程序员都有一套自己写分页的流程,但是这套流程基本上都是重复的,那么…

Docker学习笔记(一)概念理解

一、什么是docker容器 Docker容器是一种轻量级、可移植的软件封装技术&#xff0c;它允许开发者将应用程序及其依赖、配置文件、运行环境等打包到一个独立的、自包含的执行单元中。容器与虚拟机相似&#xff0c;都提供了隔离的运行环境&#xff0c;但容器更加轻量级&#xff0c…

如何清理电脑内存?让电脑运行如飞!

电脑内存&#xff08;RAM&#xff09;的清理对于维持系统的流畅运行至关重要。随着使用时间的增加&#xff0c;系统内存会被各种应用程序和后台进程占用&#xff0c;导致系统响应变慢&#xff0c;甚至出现卡顿现象。通过有效地清理内存&#xff0c;可以提升电脑的性能&#xff…

深入理解如何撤销 Git 中不想提交的文件

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

MySQL内存使用率高且不释放问题排查与总结

背景 生产环境mysql 5.7内存占用超过90%以上&#xff0c;且一直下不来。截图如下&#xff1a; 原因分析 1、确定mysql具体的占用内存大小&#xff0c;通过命令&#xff1a;cat /proc/Mysql进程ID/status查看 命令执行后的结果比较多&#xff08;其他参数的含义想了解可参考这…