tomcat-类加载

107 阅读2分钟

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: 打破双亲委派,先由子类加载,子类加载不到再由父类加载