author
type
status
date
slug
summary
tags
category
icon
password

为什么要打破双亲委派模型?如何打破?

JDBC

JDBC4.0之后使用SPI机制自动加载数据库驱动,JDBC的SPI接口(如DriverManagerDriver)由启动类加载器加载,而具体的SPI实现即不同厂商数据库驱动(如MySQL的com.mysql.cj.jdbc.Driver)位于应用类路径需由应用类加载器加载。但根据双亲委派的一般性规则,父类加载器无法委派子类加载器,若严格遵守该规则,DriverManager将无法加载第三方驱动实现
为解决此类问题,引入了线程上下文类加载器(ContextClassLoader)父加载器可以请求子类加载器去完成类加载的动作,具体做法为在ServiceLoader.load方法中通过Thread.currentThread().getContextClassLoader()获取上下文类加载器,来加载对应的数据库驱动。这种行为可以看作双亲委派模型“被破坏”。

Tomcat

Tomcat需同时运行多个Web应用,每个应用可能依赖不同版本的库,若遵循双亲委派,所有Web应用共享同一类加载器,会导致类冲突。Tomcat通过为每个Web应用创建独立的WebappClassLoader,优先加载自身目录下的类,实现类隔离和版本共存。具体做法为自定义类加载器WebappClassLoader,重写loadClass方法,调整加载顺序,优先自己加载,核心类委派父类加载器。

为什么双亲委派从下往上,不能从上往下?

首先双亲委派是自下往上委派,自上往下去加载类。
双亲委派自下而上去委派的设计,本质是通过优先信任父加载器来确保核心类库的纯净性,同时通过层次化隔离避免类的重复加载和冲突。若采用自上而下委派的顺序,会破坏这种信任链,导致核心类被篡改的风险,增加类管理的复杂性。

关于你使用全局缓存池的想法?

双亲委派模型的设计初衷并非单纯为了缓存优化,而是为了确保类加载的安全性、唯一性和隔离性(沙箱安全),缓存是类加载器的实现细节,不是模型的设计目的。
首先,类加载器之间的命名空间是隔离的
每个类加载器都有自己的命名空间。类是由全限定名 + 加载它的类加载器唯一决定的。同一个类名,在不同的类加载器中加载,是不同的类(Class 对象)。
如果把所有类扔进一个“全局缓存池”,那么:
  • 不同类加载器加载同名类可能会冲突或被篡改
  • 无法实现应用级别的类隔离,比如 Tomcat 每个 WebApp 的类隔离机制就依赖于类加载器分层设计
其次,安全性问题
假如你能从用户代码中加载一个 java.lang.String 类来替换核心类,那么 JVM 的安全机制将被完全破坏。双亲委派机制就是为了避免“用户代码优先”污染核心类库。
统一缓存池将使这种防护机制失效。

缓存已经存在,只是范围不是全局共享,

实际上,每个类加载器内部都维护了自己的缓存池(比如 loadedClasses 这样的数据结构),一旦某个类加载器加载过某个类,它是不会重复加载的
而双亲委派中,父类加载器如果已经加载过,也只会查一次缓存返回,不会重复加载或递归查找
另外委派模型实际性能损耗极低,类加载本就不是性能瓶颈,单纯从缓存角度优化,为了微乎其微的性能提升而损失基础的安全性,可能破坏这些机制,导致更严重的问题。所谓“浪费时间”的双亲委派。
 
 
系统订单的超时取消功能如何设计?Java泛型:PECS原则与Java中泛型的上下界限定符
Loading...
Sean Liu
Sean Liu
Stack the Code, Hack the World.
最新发布
深入理解Java中的双亲委派模型
2025-5-20
MySQL中一条SQL的执行过程
2025-5-20
系统订单的超时取消功能如何设计?
2025-5-20
机器学习 vs 深度学习:业务场景下的「尺长寸短」
2025-5-20
开源OCRmyPDF实战:让扫描文档重获新生的技术魔法
2025-5-20
Java泛型:PECS原则与Java中泛型的上下界限定符
2025-4-10
公告
 
We can only see a short distance ahead, but we can see plenty there that needs to be done.
目光所及之处,只是不远的前方,但那里已有太多值得去做的事在等待着我们。
 
—— Alan Turing 阿兰 · 图灵