tomcat并非彻底抛弃双亲委派,而是有选择地绕过:webappclassloader重写loadclass方法,优先本地加载web-inf资源,仅对java.*等系统类向上委派,实现类隔离与热部署。

Tomcat 并不是彻底抛弃双亲委派,而是有选择地绕过它,在关键环节做调整,从而实现 Web 应用之间的类隔离和热部署能力。
为什么必须打破双亲委派?
默认的双亲委派机制会把所有类(包括应用自己的 class 和 jar)最终交给 AppClassLoader 或 BootstrapClassLoader 加载。这会导致几个实际问题:
- 多个 Web 应用共用同一份 commons-lang、log4j 等第三方库,版本冲突无法避免
- 应用 A 修改了某个类,可能意外影响应用 B(因为类被共享加载器锁定)
- 无法单独重启或重载某个应用——类已由高层加载器加载并常驻内存
Tomcat 的类加载器分层结构
Tomcat 构建了一套非严格遵循双亲委派的加载器链:
-
BootstrapClassLoader:加载 JVM 核心类(
java.*等) -
SystemClassLoader(AppClassLoader):加载
$JAVA_HOME/lib/ext和-classpath指定路径 -
CatalinaClassLoader:加载
$CATALINA_HOME/lib下的 Tomcat 自身依赖(如 catalina.jar) -
SharedClassLoader(可选):加载
$CATALINA_HOME/lib/shared,供所有 Web 应用共享 -
WebappClassLoader(每个应用一个):最关键的一层,负责加载当前应用的
WEB-INF/classes和WEB-INF/lib/*.jar
WebappClassLoader 如何绕过委派?
它重写了 loadClass(String name, boolean resolve) 方法,做了两处关键改动:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 优先本地加载:先调用自身的
findClass(),在WEB-INF/classes和WEB-INF/lib中查找,成功就直接返回 Class 实例 - 仅对系统类向上委派:只有类名以
java.、javax.、sun.开头,或命中delegate=true配置时,才调用super.loadClass()向上委托
这意味着:com.example.User 一定由当前应用的 WebappClassLoader 加载;而 java.util.ArrayList 仍走标准路径由 Bootstrap 加载——既保障隔离,又不失安全。
隔离效果与使用边界
这种设计带来明确的运行时效果:
- 两个应用可以各自携带不同版本的
log4j-core-2.17.jar,互不影响 - 应用 A 修改
WEB-INF/classes/config.properties,不会触发应用 B 的类重载 - 停掉应用 A 后,它的 WebappClassLoader 被回收,相关类全部卸载
但要注意:跨应用共享对象(如 JNDI 绑定的 DataSource)必须使用 JDK 或 Tomcat 提供的公共 API 类型。否则即使类名相同,由不同 WebappClassLoader 加载的类在 JVM 看来仍是完全不同的类型,强转会抛出 ClassCastException。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










