文章目录
1、前言
双亲委派模型
是一种类加载机制,它确保了类加载器层次结构中的父加载器先于子加载器尝试加载类。这种机制有助于防止类的重复加载和类之间的不兼容。
然而,为了支持Web应用程序的独立性和隔离性
,Tomcat需要在某些情况下打破这一原则。
2、标准的双亲委派模型
在标准的双亲委派模型中,类加载器按以下顺序尝试加载类:
- Bootstrap ClassLoader:引导类加载器,负责加载Java核心类库($JAVA_HOME/jre/lib)。
- Extension ClassLoader:扩展类加载器,负责加载Java扩展库($JAVA_HOME/jre/lib/ext)。
- Application ClassLoader:应用类加载器,负责加载应用程序的类路径(classpath)上的类。
子类加载器在加载类时,先请求父加载器加载,只有当父加载器找不到时,子类加载器才会尝试自己加载。
3、Tomcat的类加载器架构
Tomcat为了支持多个Web应用程序的独立性,设计了一套复杂的类加载器架构,主要包括以下几个类加载器:
- Bootstrap ClassLoader:加载JVM核心类。
- System ClassLoader:加载Tomcat自身的类和库(位于$CATALINA_HOME/lib)。
- Common ClassLoader:加载共享库(位于$CATALINA_HOME/lib)。
- Webapp ClassLoader:每个Web应用都有自己的类加载器,负责加载Web应用程序的类和库(位于WEB-INF/classes和WEB-INF/lib)。
4、Tomcat打破双亲委派模型的方式
Tomcat通过
自定义类加载器
的实现,允许Web应用程序在加载类时打破双亲委派模型。这种机制允许Web应用程序优先加载自己的类和库,而不是依赖父加载器。这种行为主要通过WebappClassLoader
来实现。
具体实现:
WebappClassLoader:Tomcat为每个Web应用创建一个WebappClassLoader实例。这个类加载器会首先尝试从WEB-INF/classes和WEB-INF/lib中加载类,而不是先委派给父加载器。
双亲委派的打破:
当WebappClassLoader加载类时,它会检查是否在其自己的类路径中存在。如果存在,它会直接加载,而不是委派给父加载器。
这种机制通过在WebappClassLoader的loadClass方法中覆盖默认的双亲委派行为实现。
例如:org.apache.catalina.loader.WebappClassLoaderBase类中的findClass方法实现了优先从Web应用程序的类路径加载类。
示例代码:
以下是WebappClassLoader如何打破双亲委派模型的简化示例:
java
public class WebappClassLoader extends URLClassLoader {
private ClassLoader parent;
public WebappClassLoader(URL[] urls, ClassLoader parent) {
super(urls, null); // 将父加载器设置为null
this.parent = parent;
}
@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// 首先,检查是否已经加载了类
Class<?> clazz = findLoadedClass(name);
if (clazz == null) {
try {
// 尝试从Web应用的类路径中加载类
clazz = findClass(name);
} catch (ClassNotFoundException e) {
// 如果找不到,再委派给父加载器加载
clazz = parent.loadClass(name);
}
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
}
在这个示例中,WebappClassLoader首先尝试通过findClass方法加载类,如果找不到再委派给父加载器。这种方式打破了双亲委派模型,使得Web应用程序可以优先加载自己的类。
5、总结
Tomcat通过自定义的WebappClassLoader打破了双亲委派模型,允许Web应用程序优先加载自己的类和资源。这种机制确保了每个Web应用程序的独立性和隔离性,避免了类库冲突,并使得每个应用程序可以拥有自己的类和库版本。