tomcat通过重写webappclassloader的loadclass()方法调整双亲委派顺序:先加载本应用/web-inf/classes与lib中的类,未找到再委托父加载器;各应用独享类加载器实现类隔离;共享库由sharedclassloader统一加载;线程上下文类加载器确保框架能访问应用类。

Tomcat不是“随意破坏”双亲委派,而是有目的地**调整加载顺序**,让每个Web应用优先用自己的类加载器加载自身代码,从而实现隔离。核心在于:不放弃委托,但改变“谁先试”的逻辑。
WebAppClassLoader主动抢在父加载器前加载
标准双亲委派要求子加载器必须先把请求交给父加载器;而Tomcat的WebAppClassLoader重写了loadClass()方法,流程变成:
- 先检查本应用的
/WEB-INF/classes和/WEB-INF/lib里有没有这个类 - 如果有,直接加载——这一步就绕过了父加载器
- 只有找不到时,才按常规流程委托给父加载器(如
SharedClassLoader或CommonClassLoader)
不同应用用不同类加载器,同名类也不冲突
每个Web应用启动时,Tomcat都会创建一个独立的WebAppClassLoader实例。JVM判断两个类是否“相同”,不仅看类名和包名,还看加载它的类加载器。
比如两个应用都定义了com.example.UserController,它们被各自的WebAppClassLoader加载后,在JVM中就是两个完全独立的Class对象,互不影响、不会覆盖。
共享依赖靠父加载器统一提供
虽然Web应用自己优先加载,但像Spring、Jackson这类通用库,并不重复打包进每个应用。Tomcat通过分层类加载器设计解决复用问题:
-
CommonClassLoader加载$CATALINA_HOME/lib下的公共jar(如Tomcat自身依赖) -
SharedClassLoader加载$CATALINA_HOME/shared/lib,供所有Web应用共享 - 当
WebAppClassLoader在本地找不到Spring类时,自然委托上去,由SharedClassLoader加载一次,所有应用共用同一个Class对象
线程上下文加载器保障框架兼容性
有些第三方框架(如Spring、JDBC)运行时需要动态加载应用级类,但它们的代码是由Tomcat容器加载的,用的是CatalinaClassLoader等高层加载器,无法直接访问Web应用的类路径。
Tomcat在启动每个应用的线程时,会临时把Thread.currentThread().setContextClassLoader()设为该应用的WebAppClassLoader。这样Spring就能通过Thread.currentThread().getContextClassLoader()正确加载用户写的Bean类,而不是报ClassNotFoundException。











