Tomcat为什么要打破双亲委派?
不同web服务可能会存在相同名称的类,如果使用双亲委派,那么只会加载一个类。
Tomcat如何打破的双亲委派
首先打破双亲委派需要重写loadClass,Tomcat是在WebappClassLoader的父类WebappClassLoaderBase#loadClass中打破。
以下是打破双亲委派的流程(不委派父类进行加载,delegateLoad = false)
1. 先在本地cache查找该类是否已经加载过(Map<String,ResoureEntry>),看Tomcat有没有加载过这个类。
2. 如果Tomcat没有加载过这个类,则从系统类加载器的cache中查找是否加载过。
3. 如果没有加载过这个类,尝试用javaseLoader(ExtClassLoader)加载,防止关键类被篡改。
4. 如果未加载成功,调用#findClass加载,加载地址是:本web应用下的class,WEB-INF/classes、 WEB-INF/lib
5. 加载依然失败,使用 WebappClassLoader 的父类继续加载。
6. 都没有加载成功的话,抛出异常。
第4、5步打破了双亲委派机制,先由WebappClassLoader加载再由父类加载
不一定非要打破双亲委派,看配置
- delegateLoad - true: 不打破双亲委派,还是由父类先加载,子类加载不到也不会再由父类二次加载
- delegateLoad - false: 打破双亲委派,先由子类加载,子类加载不到再由父类加载
类加载器
Tomcat ClassLoader创建流程:
BootStrap.bat -> BootStrap#initClassLoader: 创建CommonClassLoader、ServerClassLoader、SharedClassLoader
CommonClassLoader
- Tomcat服务与WebApp服务共用;
- 父级为AppClassLoader;
CatalinaClassLoader
- Tomcat服务单独使用;
- 父级为CommonClassLoader;
- 实际等于CommonClassLoader (catalina.properties中未配置加载路径)
SharedClassLoader
- WebApp服务共用;
- 父级为CommonClassLoader;
- 实际等于CommonClassLoader (catalina.properties中未配置加载路径);
SharedClassLoader = CatalinaClassLoader = CommonClassLoader
common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
server.loader=
shared.loader=
/**
* commonClassLoader配置了加载地址,而server、shared没有配置 ,所以
* SharedClassLoader = CatalinaClassLoader = CommonClassLoader
*/
private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception {
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals("")))
return parent;
}
WebAppClassLoader
- 单个WebApp服务使用;
- 父级为CommonClassLoader
- 每个应用对应唯一的类加载器,类加载器会加载:WEB-INF/classes、 WEB-INF/lib下的class文件;
不一定非要打破双亲委派,看配置
- delegateLoad - true: 不打破双亲委派,还是由父类先加载,子类加载不到也不会再由父类二次加载
- delegateLoad - false: 打破双亲委派,先由子类加载,子类加载不到再由父类加载