双亲委派模型
双亲委派模式是在Java 1.2后引入的,
如果一个类收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器执行,如果父加载器还存在其父加载器,则进一步向上委托,依次递归,请求将最终到达顶层的启动类加载器,如果父类加载器可以完成父加载任务,就成功返回,如果父加载器无法完成加载任务,子加载器才会尝试自己去加载,这就是双亲委派模型。
类加载器和双亲委派模型
- 类加载器:负责将类文件从文件系统、网络或其他来源加载到JVM中。每个类加载器都有一个独立的类名称空间,这意味着即使两个不同的类加载器加载了同一个类,这两个类在JVM中也被视为不同的类。
类加载器分类:
启动类加载器(Bootstrap ClassLoader),是虚拟机自身的一部分,用来加载Java_HOME/lib/目录中的,或者被 -Xbootclasspath 参数所指定的路径中并且被虚拟机识别的类库;
其他类加载器:
扩展类加载器(Extension ClassLoader):负责加载<java_home style="box-sizing: border-box; outline: 0px !important;">libext目录或Java. ext. dirs系统变量指定的路径中的所有类库;
应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。
双亲委派模型:如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。
双亲委派模型的工作流程
- 委派机制:当一个类加载器收到类加载请求时,它首先不会自己尝试去加载这个类,而是把这个请求委派给父类加载器去完成。
- 递归委派:父类加载器继续向上委派,直到请求到达最顶层的启动类加载器。
- 加载请求:如果父类加载器能够完成加载任务,就成功返回;如果父类加载器无法完成加载任务(即在其搜索范围内找不到所需的类),则请求返回给子类加载器,由子类加载器尝试加载。
- 加载类:如果子类加载器也无法完成加载任务,可能会抛出
ClassNotFoundException
异常。
- 双亲委派模式的优势:
- 避免重复加载;
- 考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委派模式传递到启动加载器,而启动加载器在核心Java API中发现同名的类,发现该类已经被加载,就不会重新加载网络传递的Integer类,而直接返回已加载过的Integer.class,这样可以防止核心API库被随意篡改。
- 安全性:防止用户自定义的类加载器加载核心API的类,保证核心API的安全。
- 稳定性:避免由于用户自定义的类加载器错误而导致核心API出现问题。
- 共享性:由于类加载器的层次关系,可以保证类的唯一性,避免多个加载器加载相同的类。
打破双亲委派模型
在某些情况下,例如Web应用服务器中,可能需要打破双亲委派模型以支持热部署等特性。可以通过以下方式实现:
- 重写
loadClass
方法:在自定义类加载器中重写loadClass
方法,改变类加载的顺序,使其首先尝试自己加载类,然后再委托给父类加载器。