Java 双亲委派机制

Java 双亲委派机制

一句话总结:自己不加载,交给父级加载器加载;打破双亲委派,就是自己去加载,继承 ClassLoder 并重写 loadClass 方法。

Java 类声明周期

image-20241125131246682

Java 源文件到最终运行需要经过编译加载两个阶段。

  • 编译:Java 编译器将源代码编译为 .class(Java 字节码)文件
  • 加载:将字节码文件加载到 JVM 内存中,得到一个 Class 对象(反射常用,可用于创建对应实例)。

类加载器

JVM 提供了三类加载器

  • Bootstrap ClassLoader(引导类加载器):最高级的类加载器,加载 Java 的核心类库;会去寻找$JAVA_HOME/lib下的资源,如 java.lang, java.util, rt.jar, resource.jar。
  • Extension ClassLoader(扩展类加载器):加载对核心类库的扩展,查找$JAVA_HOME/lib/ext
  • Application ClassLoader(应用类加载器):当前应用(class path)的所有 jar 包和类文件,即程序员编写的代码和第三方类库。

除了Bootstrap ClassLoader 是C/C++实现,其他类加载器都继承了java.lang.ClassLoader

此外可以通过继承java.lang.ClassLoader来实现自定义的类加载器。

父委托模型

类加载器之间具有层级关系,如下图。这个层级并不是通过继承实现的,而是子加载器使用一个字段parent保存了父加载器的变量(组合)。

image-20241125133318339

具体的加载过程即是按照类加载器的关系由下至上逐层进行委派。父加载器无法加载时,子加载器才会尝试用自己的规则加载。

image-20241125133643297

优点

  • 安全:为类加载过程提供了优先级,可以避免核心类库的类被覆盖。
  • 可以避免重复加载:一个类(根据限定名区分,如com.keats.test.A)只会被加载一次(首次被 new 或调用静态方法时)。
    • 每个类加载器都有一个内部缓存,存储已经加载的类。
    • 类加载器在 findLoadedClass() 中会检查缓存,如果该类已经加载过,直接返回缓存中的类对象,而不会重复加载。

破坏双亲委派

通过重写自定义类中的loadClass 方法,可以破坏双亲委派原则。破坏双亲委派机制的常见场景如 Java 应用服务器中的插件机制、OSGi 框架等。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class CustomClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// 不再委派给父加载器,直接使用自定义加载方式
return findClass(name);
}

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 自定义的类加载逻辑,省略细节
return super.findClass(name);
}
}

改进是使用findClass方法。findClass()loadClass() 的最后一步,用来自行实现类的查找和加载。当父类加载器无法加载某个类时,本加载器通过 findClass() 尝试加载该类。


Java 双亲委派机制
https://buttering.github.io/EasyBlog/2024/11/25/Java 双亲委派机制/
作者
Buttering
发布于
2024年11月25日
许可协议