Java双亲委托机制:类加载的安全保障

Java中的双亲委托机制是一种类加载器的工作模式,旨在确保Java类在虚拟机中的唯一性,防止类重复加载,并保护核心API的安全性。下面我们来详细了解这个机制。

类加载器的层次结构

Java中主要有四种类加载器:

  • Bootstrap ClassLoader(启动类加载器) :负责加载Java核心类库,如java.lang.Object
  • Extension ClassLoader(扩展类加载器) :加载扩展类库,通常位于jre/lib/ext目录下。
  • Application ClassLoader(应用类加载器) :加载应用程序的类,通常是用户编写的类。
  • User ClassLoader(用户自定义类加载器) :用户可以自定义的类加载器,用于特殊场景。

每个类加载器都有一个父类加载器,除了Bootstrap ClassLoader,它没有父类加载器。

委托加载过程

  1. 请求委托:当一个类加载器收到类加载请求时,它不会直接加载,而是将请求委托给其父类加载器。
  2. 逐级委托:父类加载器继续将请求委托给其父类加载器,直到Bootstrap ClassLoader。
  3. 尝试加载:如果Bootstrap ClassLoader无法加载该类,则会逐层向下委托给子类加载器尝试加载。
  4. 自行加载:只有当父类加载器无法加载时,子类加载器才会尝试自己加载该类。

示例代码

下面是一个简单的自定义类加载器示例,展示如何重写loadClass()方法:

java 复制代码
java
import java.lang.reflect.Method;

public class CustomClassLoader extends ClassLoader {
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        // 首先尝试通过父类加载器加载
        try {
            return super.loadClass(name, resolve);
        } catch (ClassNotFoundException e) {
            // 如果父类加载器无法加载,则尝试自己加载
            try {
                byte[] classBytes = getClassBytes(name); // 自定义方法获取类字节码
                return defineClass(name, classBytes, 0, classBytes.length);
            } catch (Exception ex) {
                throw new ClassNotFoundException(name, ex);
            }
        }
    }

    // 自定义方法获取类字节码
    private byte[] getClassBytes(String className) {
        // 这里需要实现获取类字节码的逻辑,例如从文件系统或网络读取
        // 这里省略具体实现
        return null;
    }

    public static void main(String[] args) throws Exception {
        CustomClassLoader customClassLoader = new CustomClassLoader();
        Class<?> clazz = customClassLoader.loadClass("CustomClass");
        Method method = clazz.getMethod("customMethod");
        Object instance = clazz.newInstance();
        method.invoke(instance);
    }
}

双亲委托机制的作用

  • 防止类重复加载:通过委托父类加载器检查是否已经加载过该类,避免重复加载。
  • 保证核心API安全性:防止开发者覆盖核心类库中的类,因为这些类最终都是由Bootstrap ClassLoader加载的。

打破双亲委托机制

如果需要打破双亲委托机制,可以通过自定义类加载器并重写loadClass()方法来实现。这种方法通常用于特殊场景下需要绕过双亲委托的限制。

示例:打破双亲委托

下面是一个打破双亲委托的示例,直接在子类加载器中加载类,而不委托给父类加载器:

scala 复制代码
java
public class BreakDelegationClassLoader extends ClassLoader {
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        // 直接尝试自己加载,而不委托给父类加载器
        try {
            byte[] classBytes = getClassBytes(name); // 自定义方法获取类字节码
            return defineClass(name, classBytes, 0, classBytes.length);
        } catch (Exception e) {
            throw new ClassNotFoundException(name, e);
        }
    }

    // 自定义方法获取类字节码
    private byte[] getClassBytes(String className) {
        // 这里需要实现获取类字节码的逻辑,例如从文件系统或网络读取
        // 这里省略具体实现
        return null;
    }
}

这种方式通常用于特殊场景,如动态加载类或需要绕过双亲委托的限制时。

相关推荐
yuriy.wang2 分钟前
Spring IOC源码篇五 核心方法obtainFreshBeanFactory.doLoadBeanDefinitions
java·后端·spring
咖啡教室2 小时前
程序员应该掌握的网络命令telnet、ping和curl
运维·后端
徐小夕@趣谈前端2 小时前
如何实现多人协同文档编辑器
javascript·vue.js·设计模式·前端框架·开源·编辑器·github
你的人类朋友3 小时前
Let‘s Encrypt 免费获取 SSL、TLS 证书的原理
后端
老葱头蒸鸡3 小时前
(14)ASP.NET Core2.2 中的日志记录
后端·asp.net
李昊哲小课3 小时前
Spring Boot 基础教程
java·大数据·spring boot·后端
码事漫谈4 小时前
C++内存越界的幽灵:为什么代码运行正常,free时却崩溃了?
后端
Swift社区4 小时前
Spring Boot 3.x + Security + OpenFeign:如何避免内部服务调用被重复拦截?
java·spring boot·后端
90后的晨仔4 小时前
Mac 上配置多个 Gitee 账号的完整教程
前端·后端
码事漫谈4 小时前
AI智能体平台选型指南:从技术架构到商业落地的全景洞察
后端