• 双亲委派模型


    好处

    双亲委派模型是 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 方法中控制类的加载顺序,从而绕过双亲委派机制。

    1. public class CustomClassLoader extends ClassLoader {
    2. @Override
    3. protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
    4. // 先尝试自己加载
    5. try {
    6. byte[] classData = getClassData(name);
    7. if (classData != null) {
    8. return defineClass(name, classData, 0, classData.length);
    9. }
    10. } catch (Exception e) {
    11. // Ignore and fallback to parent
    12. }
    13. // 如果加载失败,委派给父类加载器
    14. return super.loadClass(name, resolve);
    15. }
    16. private byte[] getClassData(String className) {
    17. // 从文件或其他来源获取类数据
    18. return null; // 示例中返回 null
    19. }
    20. }

    2. 使用 Thread.getContextClassLoader()

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

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

    3. 重写 findClass 方法

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

    1. public class CustomClassLoader extends ClassLoader {
    2. @Override
    3. protected Class findClass(String name) throws ClassNotFoundException {
    4. byte[] classData = getClassData(name);
    5. if (classData == null) {
    6. throw new ClassNotFoundException();
    7. }
    8. return defineClass(name, classData, 0, classData.length);
    9. }
    10. private byte[] getClassData(String className) {
    11. // 从文件或其他来源获取类数据
    12. return null; // 示例中返回 null
    13. }
    14. }

    4. 使用 URLClassLoader

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

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

    注意事项

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

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

  • 相关阅读:
    ctfshow-web11(session绕过)
    辽宁2022农民丰收节 国稻种芯:4个主会场31个分会场同步
    PHP房贷计算器算法
    内连接和外连接
    双指针问题(中)
    spring boot 过滤器&拦截器与aop
    【MySQL】查询优化
    HCIP第十八天笔记
    kubenetes-pod高可用
    Java岗八年多开发经验分享
  • 原文地址:https://blog.csdn.net/zbh1957282580/article/details/139498338