上文说到,在初始化完catalina.home和catalina.base目录后,又定义了三个类加载器,这三个类加载器还是挺重要的,三个类加载器分别是:
CommonLoader
、CatalinaLoader
、SharedLoader
思考一个问题,为什么要定义三个类加载器?
在《深入理解java虚拟机》这本书中,书中解释到tomcat作为一个主流的web服务器,那么一个功能健全的web服务器,需要解决以下几个问题:
1.部署在同一个服务器上的两个web应用程序所使用的java类库可以实现相互隔离。这是最基本的要求,因为两个web应用使用到的类库版本可能不同。
2.部署在同一个服务器上的两个web应用程序所使用的java类库可以相互共享。如果一个服务器上n个项目使用到的类库在使用时都被**独立地加载
**到服务器内存,那么虚拟机的方法区就会很容易地出现过度膨胀的风险。
3.服务器需要尽可能地保证自身安全不受部署的web应用应用程序影响。目前来说,有许多主流的Java Web服务器自身也是使用java语言来实现的。因此,服务器本身也有类库依赖的问题,所以基于安全考虑,服务器所使用的类库应该与应用程序的类库互相独立。
4.支持JSP应用的web服务器,大多数也需要支持HotSwap功能。我们知道JSP文件最终都还是要靠编译成class文件交给虚拟机执行,但是JSP文件由于其纯文本存储的特性,运行时修改的概率远远大于第三方类库或程序自身的Class文件。因此主流的web服务器都会支持由JSP生成类的热替换。
从而,由于上述问题的存在,单纯的靠一个ClassPath就无法满足需求了,所以各种服务器(tomcat也是如此)就提供了好几个ClassPath路径供用户存放第三方类库。
在tomcat目录结构中,有三组目录(common、server、shared)可以存放java类库,另外还可以加上web应用程序自身的目录/WEB-INF/共四组,每个目录的含义如下表:
/common | /server | /shared | /WebApp/WEB-INF |
---|---|---|---|
类库可被Tomcat和所有的Web应用程序共同使用 | 类库可被Tomcat单独使用,对所有的的Web应用程序都不可见 | 类库可被所有的Web应用程序共同使用,但是对Tomcat不可见 | 类库仅仅是被此Web应用程序使用,对Tomcat和其它Web应用程序都不可见 |
为了支持这套目录结构,并对这些目录里的类库进行加载和隔离,tomcat才子定义了多个类加载器,这些类加载器按照经典的双亲委派模型来实现。下图是它们之间的关系:
但是自从tomcat6开始,common/server/shared这三个目录就已经合并了,当然你也可以在项目中创建这几个目录并修改配置文件信息来实现该结构。接下来我们看看源码中是如何初始化这几个类加载器的。
private void initClassLoaders() {
try {
/**
* 创建common类加载器,根据conf配置文件中的catalina.properties中的key:common.loader
* 可得出该类加载器负责加载base或者home路径下的lib文件夹下的类以及jar
*/
commonLoader = createClassLoader("common", null);
if( commonLoader == null ) {
// no config file, default to this loader - we might be in a 'single' env.
commonLoader=this.getClass().getClassLoader(