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

news2024/12/27 12:13:45

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 loaded
        Class<?> c = findLoadedClass(name);             //检查类有没有被加载
        if (c == null) {                                //类没有被加载 进入
            long t0 = System.nanoTime();
            try {
                if (parent != null) {                   //还有父加载器的话,让父加载器loadClass   这里也就是ExtClassLoader
                    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;
    }
}

进入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 loaded
        Class<?> 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 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;
    }
}

之后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 loaded
        Class<?> c = findLoadedClass(name);             //检查类有没有被加载
        if (c == null) {                                //类没有被加载 进入
            long t0 = System.nanoTime();
            try {
                if (parent != null) {                   //还有父加载器的话,让父加载器loadClass   这里也就是ExtClassLoader
                    c = 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 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;
    }
}

下面跟进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.class
        c.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.coloradmin.cn/o/1893465.html

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

相关文章

【Mac】draw.io for Mac(流程图绘制工具)及同类型软件介绍

软件介绍 Draw.io&#xff08;现在称为diagrams.net&#xff09;是一个流行的开源图表软件&#xff0c;可以帮助用户创建各种类型的图表和图形&#xff0c;如流程图、组织结构图、网络图、UML图、平面设计图等等。它最初作为一个Web应用程序推出&#xff0c;后来也推出了桌面版…

推荐一个私有化部署的物联网平台

引言 随着物联网技术的飞速发展&#xff0c;越来越多的企业开始寻求能够提供稳定、安全、可定制的物联网解决方案。私有化部署的物联网平台因其能够满足企业对数据安全和个性化需求的优势&#xff0c;逐渐成为市场的新宠。本文将详细介绍ThingsKit物联网平台&#xff0c;一个专…

代谢组数据分析(十二):岭回归、Lasso回归、弹性网络回归构建预测模型

欢迎大家关注全网生信学习者系列: WX公zhong号:生信学习者Xiao hong书:生信学习者知hu:生信学习者CDSN:生信学习者2介绍 在代谢物预测模型的构建中,我们采用了三种主流的回归分析方法:岭回归、Lasso回归以及弹性网络回归。这三种方法各有其独特的原理和适用场景,因此在…

pnpm的坑

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

C语言自定义类型(结构体,枚举,联合):

大家好久不见&#xff0c;今天我们来学习一下C语言中的自定义类型&#xff1a; C语言的自定义类型包括&#xff1a;结构体&#xff0c;枚举和联合&#xff0c;接下来大家跟我来一起认识一下这三种类型。 目录 1. 结构体 1.1.1 结构体类型的声明 1.1.2 结构的特殊声明 1.1…

nginx的重定向(rewrite)

nginx的重定向&#xff08;rewrite&#xff09; location 匹配 location匹配的就是后面的URI /wordpress 192.168.60.20/wordpress location匹配的分类和优先级 1、精确匹配 location / 对字符串进行完全匹配&#xff0c;必须完全符合 2、正则匹配 ^~ 前缀匹配&#x…

为本地化准备营销材料的几个步骤

为本地化准备营销材料涉及几个关键步骤&#xff0c;以确保内容在文化上合适、语言上准确&#xff0c;并与目标受众相关。以下是五个基本步骤&#xff1a; 进行市场调查 了解目标市场至关重要。进行深入研究&#xff0c;以收集有关目标地区受众的文化细微差别、消费者行为、地…

脑启发设计:人工智能的进化之路

编者按&#xff1a;你可以用左手&#xff08;不常用的那只手&#xff09;的小指与食指拿起一件物品么&#xff1f; 试完你是不是发现自己竟然可以毫不费力地用自己不常用的手中&#xff0c;两根使用频率相对较低的手指&#xff0c;做一个不常做的动作。这就是人类大脑不可思议…

MySQL之聚簇索引和非聚簇索引

1、什么是聚簇索引和非聚簇索引&#xff1f; 聚簇索引&#xff0c;通常也叫聚集索引。 非聚簇索引&#xff0c;指的是二级索引。 下面看一下它们的含义&#xff1a; 1.1、聚集索引选取规则 如果存在主键&#xff0c;主键索引就是聚集索引。如果不存在主键&#xff0c;将使…

高校搭建AIGC新媒体实验室,创新新闻教育教学模式

高校作为人才培养的重要阵地&#xff0c;必须紧跟时代步伐&#xff0c;不断创新教育教学模式&#xff0c;提升跨界融合育人水平&#xff0c;通过AIGC新媒体实验室探索创新人才培养模式。AIGC新媒体实验室不仅能够高效赋能高校宣传媒体矩阵&#xff0c;也可以助力教学实践与AIGC…

常见问题记录

conda操作 conda精确查找某个包的版本 conda list 包名下载源 -i https://pypi.mirrors.ustc.edu.cn/simple/conda查看下载源 conda config --show channels下载torch conda install pytorch1.13.0 torchvision0.14.0 torchaudio0.13.0 cpuonlyconda环境没有名字 利用vsc…

改变conda创建虚拟环境时的默认路径(C盘)

①C:\Users\Lenovo 找到C盘中用户目录下的.condarc文件 ②打开.condarc文件后&#xff0c;添加并修改.condarc 中的 env_dirs 环境路径&#xff0c;按顺序第⼀个路径作为默认存储路径&#xff0c;也就是我的E盘&#xff08;需要你先在E盘中新建文件夹envsE&#xff09;。修改完…

算法体系-25 第二十五节:窗口内最大值或最小值的更新结构

一 滑动窗口设计知识点 滑动窗口是什么&#xff1f; 滑动窗口是一种想象出来的数据结构&#xff1a; 滑动窗口有左边界L和有边界R 在数组或者字符串或者一个序列上&#xff0c;记为S&#xff0c;窗口就是S[L..R]这一部分 L往右滑意味着一个样本出了窗口&#xff0c;R往右滑意味…

【免费可视化工具】助力风电行业智能化管理

在绿色能源日益成为全球共识的今天&#xff0c;风电作为清洁能源的重要组成部分&#xff0c;正以前所未有的速度发展。然而&#xff0c;随着风电场规模的扩大和数量的增加&#xff0c;如何高效、直观地管理和监控风电资源成为了一个亟待解决的问题。 而山海鲸可视化这款免费可…

使用微信开发者工具连接gitee

编写代码 打开微信开发者工具 编写小程序代码 提交代码 在微信开发者工具提交代码到gitee仓库的步骤&#xff1a; 1.在gitee创建仓库&#xff0c;得到仓库url 2.微信开发者工具设置远程仓库 点击版本管理-->点击设置-->网络和认证-->认证方式选择 使用用户名和…

Windows系统安装分布式搜索和分析引擎Elasticsearch与远程访问详细教程

文章目录 前言系统环境1. Windows 安装Elasticsearch2. 本地访问Elasticsearch3. Windows 安装 Cpolar4. 创建Elasticsearch公网访问地址5. 远程访问Elasticsearch6. 设置固定二级子域名 前言 本文主要介绍如何在Windows系统安装分布式搜索和分析引擎Elasticsearch&#xff0c…

怎么录制电脑内部声音?好用的录音软件分享,看这篇就够了!

如何录制电脑内部声音&#xff1f;平时使用电脑工作&#xff0c;难免会遇到需要录音的情况。好用的录音软件有很多&#xff0c;也有部分录屏工具也支持录音功能。 那么如何录制电脑内部声音呢&#xff1f;本文整理了几个录制电脑内部声音的方法&#xff0c;如果你需要在电脑上录…

全网都在疯传的最新蓝海风口项目!

最近全网都在疯传这种视频&#xff0c;想必兄弟们都见到过了&#xff01; 大家看这个号&#xff0c;1天的时间&#xff0c;2个作品&#xff0c;第2个直接就爆了&#xff0c;昨天看点赞还是3.8w&#xff0c;今天已经10w了&#xff0c;这是妥妥的风口啊&#xff01; 大家有没有想…

Qt 进程间通信(一)——QSharedMemory共享内存

QSharedMemory共享内存 序言环境理论—逻辑理解实战—代码读取示例写入示例 序言 讲讲Qt的共享内存吧&#xff0c;巩固下 环境 msvc2022 Qt5.15 参考文档&#xff1a;https://doc.qt.io/qt-5/qsharedmemory.html 理论—逻辑理解 看下面前&#xff0c;你需要将共享内存看成…

数字研发·驱动变革 | 2024达索系统装备行业数字化研发专题研讨会成功举办

2024年6月28日&#xff0c;由百世慧举办的“数字研发驱动变革|2024达索系统装备行业数字化研发专题研讨会”在达索系统&#xff08;重庆&#xff09;智能制造创新中心成功举办。 随着全球制造业向着智能化、数字化转型&#xff0c;我国工业装备行业也面临着转型升级的压力和机遇…