Tomcat通过自定义WebappClassLoader并重写loadClass方法打破双亲委派,实现应用间类隔离与热部署:优先本地加载WEB-INF资源,仅对java.*等系统类向上委派。

Tomcat 通过自定义类加载器并主动打破双亲委派模型,实现 Web 应用间的类隔离和热部署能力。核心不是“完全抛弃”双亲委派,而是在关键环节进行有控制的绕过。
为什么需要破坏双亲委派?
默认的双亲委派机制会把所有类(包括应用自己的 jar 和 class)最终交给 AppClassLoader 或 BootstrapClassLoader 加载,导致:
- 多个 Web 应用共用同一份
commons-lang等第三方库,版本冲突无法避免 - 一个应用修改了某个类,可能意外影响其他应用(类被共享)
- 无法独立重启/重载某个应用(类已由高层加载器锁定)
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(每个应用一个):最关键的一层,它重写了loadClass()
WebappClassLoader 怎么打破委派?
它在 loadClass(String name, boolean resolve) 中做了两处关键改动:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
立即学习“Java免费学习笔记(深入)”;
-
优先本地加载:先尝试用自己的
findClass()在WEB-INF/classes和WEB-INF/lib/*.jar中查找,成功则直接返回 -
仅对系统类走委派:若类名以
java.、javax.、sun.等开头,或命中delegate配置列表,则才调用super.loadClass()向上委托
也就是说:你的 com.example.User 一定由当前 Web 应用的 WebappClassLoader 加载;而 java.util.ArrayList 仍走标准委派到 Bootstrap 加载——既隔离又安全。
隔离效果与注意事项
这种设计带来实际隔离效果:
- 两个应用可以各自带不同版本的
log4j-core-2.17.jar,互不影响 - 应用 A 修改
WEB-INF/classes/config.properties,不会触发应用 B 的类重载 - 停掉应用 A,它的
WebappClassLoader被回收,相关类全部卸载
但要注意:跨应用共享对象(如通过 JNDI 绑定的 DataSource)必须使用 JDK 或 Tomcat 提供的公共 API 类型,否则会出现 ClassCastException(即使类名相同,但由不同类加载器加载,JVM 视为不同类)。

















