Tomcat 通过 自定义类加载器 WebAppClassLoader
打破双亲委派机制,核心逻辑是 优先从 Web 应用本地目录加载类,而非直接委托父加载器。具体实现如下:
- 重写
loadClass
方法
Tomcat 的WebAppClassLoader
覆盖了ClassLoader
的loadClass
方法,默认流程为:
-
- 先检查本地缓存(已加载的类)。
- 尝试通过
ExtClassLoader
加载(防止 Web 应用覆盖 JVM 核心类,如java.lang.Object
)。 - 在 Web 应用目录(
WEB-INF/classes
和WEB-INF/lib
)中查找类。 - 若本地未找到,再委托给父加载器(如
AppClassLoader
)。
- 类加载顺序的调整
默认双亲委派是“父优先”,而 Tomcat 的WebAppClassLoader
通过以下调整实现“子优先”:
// 简化代码示例(实际逻辑更复杂)
public Class<?> loadClass(String name) {
// 1. 检查本地是否已加载
Class<?> clazz = findLoadedClass(name);
if (clazz == null) {
// 2. 优先从 Web 应用目录加载
clazz = findClass(name);
if (clazz == null) {
// 3. 委托父加载器加载
clazz = super.loadClass(name);
}
}
return clazz;
}
这种设计使得 Web 应用可以覆盖容器或 JDK 的类(如日志框架),但核心类(如 java.lang.String
)仍由父加载器加载
Tomcat 的类加载顺序分为 多层级加载策略,具体如下:
- 类加载器层级
-
- Bootstrap ClassLoader:加载 JVM 核心类(
JAVA_HOME/jre/lib
)。 - System ClassLoader:加载 Tomcat 启动类(如
catalina.jar
)。 - Common ClassLoader:加载 Tomcat 公共类(
$CATALINA_HOME/lib
)。 - Shared ClassLoader:加载所有 Web 应用共享的类。
- WebAppClassLoader:每个 Web 应用独立的类加载器,加载
WEB-INF/classes
和WEB-INF/lib
。
- Bootstrap ClassLoader:加载 JVM 核心类(
- 具体加载流程
当 Web 应用请求加载类时,顺序为:
-
- 检查本地缓存(
WebAppClassLoader
)。 - 委托
ExtClassLoader
加载(防止覆盖核心类)。 - 在
WEB-INF/classes
中查找类。 - 在
WEB-INF/lib
的 JAR 包中查找类。 - 委托
Common
→Shared
→System
→Bootstrap
加载
- 检查本地缓存(