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) 中做了两处关键改动:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
-
优先本地加载:先尝试用自己的
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 视为不同类)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










