Tomcat 并非破坏双亲委派,而是重新定义加载优先级:WebAppClassLoader 采用本地优先策略,先加载本应用的 WEB-INF/classes 和 lib,失败后才委托父加载器,实现应用间类隔离与共享类复用,并通过线程上下文类加载器使框架可访问应用类。

Tomcat 并不是“破坏”双亲委派,而是**重新定义加载优先级**,用分层 + 本地优先的方式,在隔离与共享之间取得平衡。
每个 Web 应用独享一个 WebAppClassLoader
Tomcat 启动时,为每个应用(如 app1、app2)创建独立的 WebAppClassLoader 实例。JVM 判定类是否相同,不仅看全限定名(如 com.example.User),还必须类加载器一致:
- app1 的 com.example.User 由其 WebAppClassLoader 加载 → 是 Class A
- app2 的同名类由另一个 WebAppClassLoader 加载 → 是 Class B
- 两者静态变量、初始化块、Class.forName() 结果完全不互通
本地优先加载:真正起效的关键逻辑
WebAppClassLoader 重写了 loadClass(String, boolean),执行顺序是:
- 先查本应用已加载的类(findLoadedClass)
- 再扫描 WEB-INF/classes(.class 文件)
- 再扫描 WEB-INF/lib/*.jar(含 Spring、Jackson 等依赖)
- 仅当以上全部失败,才调用 super.loadClass 委托给父加载器
这就让 app1 可以自带 slf4j-api-1.7.30.jar,app2 自带 slf4j-api-2.0.9.jar,互不影响。
立即学习“Java免费学习笔记(深入)”;
共享类由父加载器统一供给,避免重复加载
不是所有类都要隔离。像 javax.servlet.Servlet、org.springframework.context.ApplicationContext 这类通用依赖,靠分层加载器复用:
- CommonClassLoader:加载 $CATALINA_HOME/lib/,供 Tomcat 内核和所有应用共享(如 Servlet API)
- SharedClassLoader(可选):加载 $CATALINA_HOME/shared/lib/,供多个应用共用工具包
- WebAppClassLoader 找不到时才委托,确保 Spring 类只加载一次,所有应用拿到的是同一个 Class 对象
线程上下文类加载器保障框架可访问应用类
Spring、JDBC 等框架代码由 Tomcat 加载(使用 CatalinaClassLoader),但运行时需加载用户写的 Bean 类。Tomcat 在处理每个请求时,会临时设置:
Thread.currentThread().setContextClassLoader(webAppClassLoader)
这样 Spring 就能通过 Thread.currentThread().getContextClassLoader() 正确加载 WEB-INF/classes 中的业务类,不会报 ClassNotFoundException。


















