::我的理解
本质上,就是对类的加载顺序的问题。好处在于
- 避免类的重复加载。
笔记
避免类的重复加载,JVM中区分不同类,不仅仅是根据类名,相同的class文件被不同的ClassLoader加载就属于两个不同的类(比如,Java中的Object类,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,如果不采用双亲委派模型,由各个类加载器自己去加载的话,系统中会存在多种不同的Object类)
- 保护程序安全,防止核心API被随意篡改
笔记
保护程序安全,防止核心API被随意篡改,避免用户自己编写的类动态替换 Java的一些核心类,比如我们自定义类:java.lang.String
执行过程
技巧💡
先使用父类加载器,如果失败在到子类加载器。
- 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;
- 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器;
- 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式
破坏双亲委派机制
- 双亲委派模型并不是一个强制性的约束模型,而是Java设计者推荐给开发者的类加载器实现方式,可以“被破坏”,只要我们自定义类加载器,重写loadClass()方法,指定新的加载逻辑就破坏了,重写findClass()方法不会破坏双亲委派。
- 双亲委派模型有一个问题:顶层ClassLoader,无法加载底层ClassLoader的类。典型例子JNDI、JDBC,所以加入了线程上下文类加载器(Thread Context ClassLoader),可以通过
Thread.setContextClassLoaser()
设置该类加载器,然后顶层ClassLoader再使用Thread.getContextClassLoader()
获得底层的ClassLoader进行加载。 - Tomcat中使用了自定ClassLoader,并且也破坏了双亲委托机制。每个应用使用WebAppClassloader进行单独加载,他首先使用WebAppClassloader进行类加载,如果加载不了再委托父加载器去加载,这样可以保证每个应用中的类不冲突。每个tomcat中可以部署多个项目,每个项目中存在很多相同的class文件(很多相同的jar包),他们加载到jvm中可以做到互不干扰。
- 利用破坏双亲委派来实现**代码热替换**(每次修改类文件,不需要重启服务)。因为一个Class只能被一个ClassLoader加载一次,否则会报
java.lang.LinkageError
。当我们想要实现代码热部署时,可以每次都new一个自定义的ClassLoader来加载新的Class文件。JSP的实现动态修改就是使用此特性实现。