Tomcat的类加载器
一.Jvm的类加载器
编辑
- Bootstrap:用于加载JVM提供的基础运行类,即位于%JAVA_HOME%/jre/lib目录下的核心类库。
- Extension: Java提供的一个标准的扩展机制用于加载除核心类库外的Jar包, 即只要复制到指定的扩展目录(可以多个)下的Jar,JVM会自动加载(不需要通过-classpath指定)。默认的扩展目录是%JAVA_HOME%/jre/lib/ext。典型的应用场景就是,Java使用该类加载器加载JVM默认提供的但是不属于核心类库的Jar,如JCE R等。不推荐将应用程序依赖的类库放置到扩展目录下,因为该目录下的类库对所有基于该JVM运行的应用程序可见。
- System(AppClassLoader):用于加载环境变量CLASSPATH(不推荐使用)指定目录下的或者-classpath运行参数指定的Jar包。System类加载器通常用于加载应用程序Jar包及其启动人口类(Tomcat的Bootstrap类即由System类加载器加载)。
二.Tomcat的类加载器
编辑
- Common:以System为父类加载器,是位于Tomcat应用服务器顶层的公用类加载器。其路径为common.loader,默认指向$CATALINA_HOME/lib下的包。
- Catalina: 以Common为父加载器,是用于加载Tomcat应用服务器的类加载器,其路径为server.loader,默认为空。此时Tomcat使用Common类加载器加载应用服务器。
- Shared:以Common为父加载器,是所有Web应用的父加载器其路径为shared.loader ,默认为空。此时Tomcat使用Common类加载器作为Web应用的父加载器。
- Web应用:以Shared为父加载器,加载/WEB-INF/classes目录下的未压缩的Class和资源文件以及/WEB-INF/lib目录下的Jar包。如前所述,该类加载器只对当前Web应用可见,对其他Web应用均不可见。
从Web应用程序的角度来看,类或资源的加载按以下顺序查找以下存储库
- Bootstrap classes of your JVM
- /WEB-INF/classes of your web application
- /WEB-INF/lib/*.jar of your web application
- System class loader classes (described above)
- Common class loader classes (described above)
三.Tomcat类加载器这样设计的优点
应用服务器通常会自行创建类加载器以实现更灵活的控制,这一方面是对规范的实现(Servlet规范要求每个Web应用都有一个独立的类加载器实例),另一方面也有架构层面的考虑。
- 隔离性: Web应用类库相互隔离,避免依赖库或者应用包相互影响。设想一下,如果我们有两个Web应用,一个采用了Spring2.5,一个采用了Spring 4.0,而应用服务器使用一个类加载器加载,那么Web应用将会由于Jar包覆盖而导致无法启动成功。
- 灵活性:既然Web应用之间的类加载器相互独立,那么我们就能只针对一个Web应用进行重新部署,此时该Web应用的类加载器将会重新创建,而且不会影响其他Web应用。如果采用一个类加载器,显然无法实现,因为只有一个类加载器的时候,类之间的依赖是杂乱无章的,无法完整地移除某个Web应用的类。
- 性能:由于每个Web应用都有一个类加载器,因此Web应用在加载类时,不会搜索其他 Web应用包含的Jar包,性能自然高于应用服务器只有一个类加载器的情况。
四.Tomcat类加载器的实现
Bootstrap:initClassLoaders();
- commonLoader = createClassLoader("common", null);
- catalinaLoader = createClassLoader("server", commonLoader);
- sharedLoader = createClassLoader("shared", commonLoader);
digester的创建:
- Catalina:createStartDigester();
- Digester digester = new Digester();
创建类的过程:
- ObjectCreateRule:begin();
- Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
- SetNextRule:end();
- IntrospectionUtils.callMethod1(parent, methodName,child, paramType, digester.getClassLoader());
- digester.getClassLoader();
- Thread.currentThread().getContextClassLoader();