双亲委派模型

好处

双亲委派模型是 Java 类加载器机制中的一种设计思想,它将类加载操作委派给父类加载器,只有在父类加载器无法加载某个类时才由子类加载器来加载。双亲委派模型的好处包括:

  1. 隔离性:通过将类加载操作委派给父类加载器,每个类加载器都有自己的命名空间,可以确保不同类加载器加载的类彼此隔离,防止类之间的冲突和混淆。

  2. 避免重复加载:双亲委派模型可以避免同一个类被多个类加载器重复加载。当一个类被加载后,它会被缓存到父类加载器的命名空间中,后续相同的类加载请求会直接返回缓存的类,避免了重复加载和内存浪费。

  3. 安全性:通过双亲委派模型,Java 运行时环境可以确保核心类库的安全性和一致性。核心类库由启动类加载器加载,其他类都委托给父类加载器加载,从而确保了核心类库的完整性和安全性。

  4. 减少类加载器冲突:双亲委派模型使得类加载器之间的关系变得清晰和有序,可以有效地避免类加载器冲突和类重复加载的问题。每个类加载器都有自己的父类加载器,通过委派机制保证了类加载的有序性和一致性。

有哪些框架用到了双亲委派模型

  • Java 核心类库:Java 核心类库是由启动类加载器加载的,它们的加载遵循双亲委派模型。

  • Servlet 容器:例如 Tomcat、Jetty 等 Servlet 容器通常会使用双亲委派模型加载 Web 应用程序中的类和资源。

  • Spring 框架:Spring 框架是一个广泛使用的 Java 开发框架,它的核心模块和依赖库都会受到双亲委派模型的影响。

  • Hibernate ORM:Hibernate 是一个流行的对象关系映射框架,它也会利用 Java 类加载器的双亲委派模型来加载实体类和持久化对象。

  • Apache Commons 系列:Apache Commons 是一系列常用的 Java 工具库,例如 Apache Commons Lang、Apache Commons IO 等,它们也会使用到双亲委派模型。

  • JUnit 测试框架:JUnit 是一个流行的 Java 单元测试框架,它的加载过程也会受到双亲委派模型的影响。

  • Log4j 日志框架:Log4j 是一个常用的 Java 日志框架,它的加载过程也会遵循双亲委派模型。

如何打破双亲委派机制

双亲委派机制是一种 Java 类加载机制,它保证了 Java 类加载的一致性和安全性。在这种机制下,当一个类加载器收到类加载请求时,它会首先将请求委派给父类加载器进行加载,如果父类加载器无法完成加载任务,才会由当前类加载器进行加载。这个机制确保了 Java 核心类库(如 java.lang.Object)不会被重写或篡改。

虽然双亲委派机制有其重要性,但在某些特殊场景下,可能需要打破这种机制。例如,当需要加载不同版本的库时,或者进行插件化开发时,可能需要自定义类加载器来打破双亲委派机制。以下是几种打破双亲委派机制的方法:

1. 自定义类加载器

通过自定义类加载器,可以在 loadClass 方法中控制类的加载顺序,从而绕过双亲委派机制。

public class CustomClassLoader extends ClassLoader {
    
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        // 先尝试自己加载
        try {
            byte[] classData = getClassData(name);
            if (classData != null) {
                return defineClass(name, classData, 0, classData.length);
            }
        } catch (Exception e) {
            // Ignore and fallback to parent
        }
        
        // 如果加载失败,委派给父类加载器
        return super.loadClass(name, resolve);
    }

    private byte[] getClassData(String className) {
        // 从文件或其他来源获取类数据
        return null;  // 示例中返回 null
    }
}

2. 使用 Thread.getContextClassLoader()

在某些情况下,可以通过设置线程上下文类加载器来绕过双亲委派机制。线程上下文类加载器可以通过 Thread.setContextClassLoader() 来设置,并通过 Thread.getContextClassLoader() 来获取。

Thread.currentThread().setContextClassLoader(new CustomClassLoader());

3. 重写 findClass 方法

自定义类加载器时,可以重写 findClass 方法,而不是 loadClass 方法。这种方法通常在需要更细粒度控制类加载过程时使用。

public class CustomClassLoader extends ClassLoader {
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        }
        return defineClass(name, classData, 0, classData.length);
    }

    private byte[] getClassData(String className) {
        // 从文件或其他来源获取类数据
        return null;  // 示例中返回 null
    }
}

4. 使用 URLClassLoader

URLClassLoader 是一种现成的类加载器,允许从 URL 指定的路径加载类。在某些场景下,可以利用 URLClassLoader 来实现类加载的灵活性,打破双亲委派机制。

URL[] urls = {new URL("file:///path/to/classes/")};
URLClassLoader urlClassLoader = new URLClassLoader(urls, null);  // null 代表不使用父类加载器
Class<?> clazz = urlClassLoader.loadClass("com.example.MyClass");

注意事项

  • 打破双亲委派机制可能会带来类加载的安全性和一致性问题,需要谨慎处理。
  • 在使用自定义类加载器时,需要考虑类的重复加载、类冲突等问题。
  • 尽量在插件系统、应用容器等需要隔离不同版本类库的场景下使用自定义类加载器。

通过以上方法,可以在必要时打破双亲委派机制,从而实现更灵活的类加载策略。

相关推荐

  1. JVM双亲委派模型

    2024-06-06 22:46:04       20 阅读
  2. 双亲委派模型

    2024-06-06 22:46:04       23 阅读
  3. 双亲委派模型

    2024-06-06 22:46:04       8 阅读
  4. 类加载器与双亲委派模型

    2024-06-06 22:46:04       35 阅读
  5. 简单聊聊类加载器双亲委派模型

    2024-06-06 22:46:04       16 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-06 22:46:04       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-06 22:46:04       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-06 22:46:04       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-06 22:46:04       20 阅读

热门阅读

  1. C++构造器设计模式

    2024-06-06 22:46:04       8 阅读
  2. 运维开发详解

    2024-06-06 22:46:04       7 阅读
  3. C++学习笔记

    2024-06-06 22:46:04       7 阅读
  4. 常微分方程 (ODE) 和 随机微分方程 (SDE)

    2024-06-06 22:46:04       12 阅读
  5. 【面试宝藏】Go并发编程面试题

    2024-06-06 22:46:04       6 阅读
  6. Linux学习—Linux环境下的网络设置

    2024-06-06 22:46:04       9 阅读
  7. 【力扣】不同的子序列

    2024-06-06 22:46:04       7 阅读
  8. c time(NULL) time(time_t *p) 区别

    2024-06-06 22:46:04       9 阅读
  9. 回溯算法全排列

    2024-06-06 22:46:04       9 阅读
  10. 数据仓库之核心模型与扩展模型分离

    2024-06-06 22:46:04       8 阅读
  11. Linux中的tar命令:打包与解包的艺术

    2024-06-06 22:46:04       9 阅读