当前位置: 首页 > article >正文

解析双亲委派机制源码

剖析ClassLoader源码,理解双亲委派机制


双亲委派机制是Java的类加载器在处理加载类的任务时的一种分配机制,可以防止类被重复加载或者出现手写类代替系统类的风险,上篇已经全面介绍了双亲委派机制,本篇我们从ClassLoader抽象类的源码上来一探究竟

上篇说到了类加载器会在加载类时先自底向上查找是否被加载过,再由顶向下进行加载,那么他究竟是如何实现的呢,我们可以查看ClassLoader抽象类的源码
ClassLoader 是 Java 类加载机制的核心部分,用于动态加载 Java 类到 JVM 中

首先找到源码

打开IDEA进入任意一个项目,双击SHIFT键即可查找,输入ClassLoader,选择"类"找到java.lang下的ClassLoader抽象类
在这里插入图片描述
其中的含有四个核心方法:
loadClass、findClass、defineClass、resolveClass,他们涉及了类加载的全部过程,进入ClassLoader抽象类按CTRL+F查找四个类名逐一查看:

  • loadClass():类加载的入口,该方法会根据 “双亲委派模型” 首先请求父类加载器加载类。如果父类加载器无法找到目标类,当前 ClassLoader 才会调用 findClass 方法来尝试加载该类。
  • findClass():在 loadClass 的加载逻辑中,如果父类加载器未能找到类,则当前类加载器通过调用 findClass 来查找类的字节码。通常用于自定义类加载器时,负责从自定义源(如文件、网络等)加载类的字节码。
  • defineClass():这个方法用于将二进制字节数组转换为 Class 对象。通常在 findClass 中被调用,用于将从外部源获取的字节码定义为 JVM 中的类。
  • resolveClass():执行类生命周期中的连接阶段

当有类加载任务时,这四个方法的执行顺序和调用关系大致为:

loadClass
父加载器加载成功?
返回已加载类
findClass
加载类字节码
defineClass
resolve?
resolveClass
返回加载类

查看源码

由于我们要探讨的是双亲委派机制的原理,所以我们本篇只解析一下loadClass方法的源码:
他的默认实现是调用重载方法传入参数resolve = false,即不需要调用resolveClass方法,也就是上图的最后一个环节

//默认实现
public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
//重载方法
    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
                    PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

所以我们重点查看重载方法loadClass(String name, boolean resolve),我们逐步分析这个代码的执行逻辑:

1. 获取类加载锁
synchronized (getClassLoadingLock(name)) 

在加载类时,getClassLoadingLock(name) 会返回与类名关联的锁对象,以确保在多线程环境中同一个类不会被多次加载

2. 检查类是否已经加载
Class<?> c = findLoadedClass(name);
if (c == null) {

源码中的注释写道:First, check if the class has already been loaded(检查类是否已经加载),目的是检查类是否已经被当前 ClassLoader 或父类加载器加载。如果类已经被加载,就不会再次加载它

3. 尝试由父类加载器加载类
if (parent != null) {
    c = parent.loadClass(name, false);
} else {
    c = findBootstrapClassOrNull(name);
}

如果类未被加载(c == null),首先会尝试使用父加载器加载类。这是 “双亲委派模型” 的体现,即优先让父类加载器处理类加载
这里如果parent != null并不是说明当前类加载器没有父类加载器,而是说明当前类加载器的父类是启动类加载器,由于启动类加载器Bootstarp是由C++编写的,java代码中无法直接引用,他会调用findBootstrapClassOrNull(name) 来查找核心类库。

4. 由当前类加载器尝试加载
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
                    PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    PerfCounter.getFindClasses().increment();
                }

如果父类加载器也没有加载成功,则由当前类加载器来进行加载,会调用findClass方法根据类的全限定名,获取类的字节码文件,但是该方法一般由子类进行重写,自定义加载路径以及加载方式

5. 解析并返回类
			if (resolve) {
                resolveClass(c);
            }
            return c;

http://www.kler.cn/news/328989.html

相关文章:

  • StarRocks 中如何做到查询超时(QueryTimeout)
  • STM32器件支持包安装,STLINK/JLINK驱动安装
  • matlab r2024a、matlab R2024b保姆级安装教程
  • 滚雪球学MySQL[1.3讲]:MySQL客户端工具:详解与实践
  • 0基础跟德姆(dom)一起学AI 机器学习02-KNN算法
  • leetcode热题100.最长公共子序列
  • OpenFeign微服务部署
  • 自动驾驶系列—自动驾驶MCU架构全方位解析:从单核到多核的选型指南与应用实例
  • 前端——测试与打包时静态资源引用路径
  • Docker学习和部署ry项目
  • 磁编码器磁铁要求和安装要求
  • 安装pymssql
  • 简单两步,Spring Boot 定时任务也能动态设置
  • Springboot3保存日志到数据库
  • 力扣题解 983
  • MySQL-联合查询
  • PHP哪种加密扩展可以生成和验证数字签名
  • CSS中字体图标的使用
  • 长效ip的特征除了稳定还有什么
  • HTTP 和 HTTPS 协议的区别?
  • JavaWeb 12.Tomcat10
  • springboot的调度服务与异步服务实操
  • 简站wordpress主题产品多图ACF插件设置方法
  • ‌WPF Prism框架的优势主要体现
  • 华为-IPv6与IPv4网络互通的6to4自动隧道配置实验
  • php的urlencode和rawurlencode区别
  • 安全服务面试
  • 【MySQL】数据库基础知识
  • 云计算Openstack Cinder
  • 教程:在Linux上启动、运行、杀掉和管理项目程序