双亲委派模型

双亲委派模型

双亲委派模型介绍

​ 类加载器有很多种,当我们想要加载一个类的时候,具体是哪个类加载器加载呢?这就需要提到双亲委派模型了。

​ 根据官网介绍:

The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself. The virtual machine’s built-in class loader, called the “bootstrap class loader”, does not itself have a parent but may serve as the parent of a ClassLoader instance.

​ 翻译过来大概的意思是:

ClassLoader 类使用委托模型来搜索类和资源。每个 ClassLoader 实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。虚拟机中被称为 “bootstrap class loader” 的内置类加载器本身没有父类加载器,但是可以作为 ClassLoader 实例的父类加载器。

从上面的介绍可以看出

  • ClassLoader 类使用委托模型来搜索类和资源
  • 双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。
  • ClassLoader 实例回在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。

下图展示的各种类加载器之间的层次关系被称为类加载器的 "双亲委派模型(Parents Delegation Model)”

类加载器层次关系图

​ 注意 ⚠️ :双亲委派模型并不是一种强制性的约束,只是 JDK 官方推荐的一种方式。如果我们因为某些特殊需求想要打破双亲委派模型也是可以的

​ 另外,类加载器之间的父子关系一般不是以继承的关系来实现的,而是通常使用组合关系来复用父加载器的代码。

​ 在面向对象编程中,有一条非常经典的设计原则:组合优于继承,多用组合少用继承

双亲委派模型的执行流程

​ 双亲委派模型的实现代码非常简单,逻辑非常清晰,都集中在 java.lang.ClassLoaderloaderClass()

​ 每当一个类加载器收到加载请求的时候,它会将请求转发给父类加载器。在父类加载器没有找到所请求类的情况下,该类加载器才会尝试去加载。

  • 在类加载的时候,系统会首先判断当前类是否被加载过,已经被加载的类会直接返回,否则才会尝试加载(每个父类加载器都会重新走一遍这个流程)
  • 类加载器进行类加载的时候,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成(调用父类加载器 loadClass() 方法来加载类)。这样的话,所有的请求最终都会传递到顶层的启动类加载器中进行加载
  • 只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子类加载器才会尝试自己去架子啊(调用自己的 findClass() 方法来加载类)
  • 如果子类加载器也无法加载这个类,那么它会抛出一个 ClassNotFoundException 异常。

🌈扩展一下:

JVM 判断两个 Java 类是否相同的具体规则 : JVM 不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者相同的情况,才认为两个类是相同的。即使两个类来源于同一个 Class 文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那么这两个类就必定不相同。

双亲委派模型的好处

​ 双亲委派模型保证了 Java 程序的稳定运行,可以避免类的重复加载,也保证了 Java 的核心 API 不被篡改。

​ 如果没有使用双亲委派模型,而是每个类加载器加载自己的话的就会出现一些问题,比如我们编写一个称为 java.lang.Object 类的话,那么程序运行时,系统就会出现两个不同的 Object 类。双亲委派模型可以保证加载的是 JRE 中的核心 API ,而不是我们自己写的 Object 类。这是因为 AppClassLoader 加载我们自己的类时候,根据双亲委派模型,会委派给启动类加载器进行加载,而启动类加载器发现自己已经加载了 Object 类,就会直接返回,不会加载我们自己写的 Object 类。

打破双亲委派模型方法

​ 自定义加载器的话,需要继承 ClassLoader 。如果我们不想打破双亲委派模型,就重写 ClassLoader 类中的 findClass() 方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。但是,如果想打破双亲委派模型则需要重写 loadClass() 方法。

为什么是重写 loadClass() 方法打破双亲委派模型呢?双亲委派模型的执行流程已经解释了:

类加载器在进行类加载的时候,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成(调用父加载器 loadClass()方法来加载类)。

去尝试加载这个类,而是把这个请求委派给父类加载器去完成(调用父加载器 loadClass()方法来加载类)。

重写 loadClass()方法之后,我们就可以改变传统双亲委派模型的执行流程。例如,子类加载器可以在委派给父类加载器之前,先自己尝试加载这个类,或者在父类加载器返回之后,再尝试从其他地方加载这个类。具体的规则由我们自己实现,根据项目需求定制化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值