双亲委派模型

类加载器

JAVA虚拟机设计团队有意把类加载阶段中的“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何获取所需的类。实现这个动作的代码被称为“类加载器”(Class Loader)。

类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远超类加载阶段。对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。这句话可以表达的更通俗一点:比较两个类是否相等。只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要他们的类加载器不同,那这两个类就不同

双亲委派模型

在Java开发人员的角度,Java一直保持着三层类加载器,双亲委派的类加载架构。JVM提供了三层类加载器:

  • 启动类加载器(Bootstrap Class Loader):负责加载<JAVA_HOME>\lib目录下的核心类,开发者不能直接引用

  • 扩展类加载器(Extension Class Loader):负责加载<JAVA_HOME>\lib\ext目录下的扩展类,开发者可以使用扩展类加载器加载Class文件

  • 应用程序类加载器(Application Class Loader):负责加载用户类路径(ClassPath)的所有类库。如果应用程序没有定义过自己的类加载器,那么这个就是程序默认的加载器。

JDK 9之前的Java程序都是由这三种类加载器相互配合完成的,用户也可以加入自定义的类加载器进行扩展,他们之间的关系如图

这种关系被称为双亲委派模型。双亲委派模型要求除了顶层的启动类加载器外,所有的类加载器都应该有自己的父类加载器,不过这里的类加载器之间父子关系不是继承来实现的,而是使用组合来复用父加载器

它的具体工作工程是:如果一个类加载器收到类加载的请求,会先交由父类加载器去加载,每个层次的类加载器都是如此,因此所有的加载请求都是先由最顶层的启动类加载器去加载,当父类加载器无法完成加载请求时,子加载器才会尝试自己完成加载

双亲委派机制的作用:

  • 避免类的重复加载

  • 防止核心类被篡改,核心类由启动类加载器进行加载,用户自定义的同名核心类不会被加载(因为最终会交给启动类加载器,而启动类加载器已经加载过同名的类)。

双亲委派的弊端

由于BootstrapClassloader是顶级类加载器,BootstrapClassloader无法委派AppClassLoader来加载类,也就是说BootstrapClassloader中加载的类中无法使用由AppClassLoader加载的类。可能绝大部分情况这个不算是问题,因为BootstrapClassloader加载的都是基础类,供AppClassLoader加载的类调用的类。但是万事万物都不是绝对的比如JNDI服务。

JNDI的代码是由启动类加载器完成加载的,是很基础的类,但是JNDI存在的目的就是对资源进行查找和集中管理,他需要调用其他厂商实现并部署在应用程序(ClassPath)下的JNDI服务提供接口(SPI)的代码。

解决

为了解决这个困境,Java设计团队引入了一个不太优雅的设计:线程上下文类加载器(Thread Context ClassLoader)。

JDK 6以后,有提供了java.util.ServiceLoader类,以META-INF/services中的配置信息,辅以责任链的模式。

posted @ 2021-09-12 21:33  刚刚好。  阅读(282)  评论(0)    收藏  举报