Tomcat是日常用到的轻量级应用服务器,Tomcat有其自己的类加载机制,理解它的类加载机制,有助于在日常工作中快速定位问题。

        在Tomcat目录结构中,有3组目录(“/common/*”“/server/*”“/shared/*”)可以存放Java类库,另外还可以加上Web应用程序自身的目 录“/WEB-INF/*”,一共4组,Java类库放置在这些目录中的含义分别如下。

        放置在/common目录中:类库可被Tomcat和所有的Web应用程序共同使用。
        放置在/server目录中:类库可被Tomcat使用,对所有的Web应用程序都不可见。
        放置在/shared目录中:类库可被所有的Web应用程序共同使用,但对Tomcat自己不可见。
        放置在/WebApp/WEB-INF目录中:类库仅仅可以被此Web应用程序使用,对Tomcat和其Web应用程序都不可见。为了支持这套目录结构,并对目录里面的类库进行加载和隔离,Tomcat自 定义了多个类加载器,这些类加载器按照经典的双亲委派模型来实现,其关系如下图所示


        灰色背景的3个类加载器是JDK默认提供的类加载器,这3个加载器的作用在第7章中已经介绍过了CommonClassLoaderCatalinaClassLoaderSharedClassLoaderWebappClassLoader则是Tomcat自 己定义的类加载器,它们分别加载/common/*/server/*/shared/*/WebApp/WEB-INF/*中的Java类库。其中 WebApp类加载器和Jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个JSP文件对应一个Jsp类加载器。
        从上图的委派关系中可以看出, CommonClassLoader能加载的类都可以被CatalinaClassLoaderSharedClassLoader使用,而CatalinaClassLoaderSharedClassLoader己能加载的类则与对方相互隔离。WebAppClassLoader可以使用SharedClassLoader加载到的类,但各WebAppClassLoader实例之间相互隔离。而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个Class,它出现的目的就是为了被丢弃:当服务器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件HotSwap功能。
        对于Tomcat6.x版本,只有指定了 tomcat/conf/catalina.properties配置文件的server.loadershare.loader项后才会真正建立CatalinaClassLoaderSharedClassLoader的实例,否则会用到这两个类加载器的地方都会用CommonClassLoader的实例代替,而默认的配置文件中没有设置这两个loader项,所以Tomcat6.x顺理成章地把/common/server/shared三个目录默认合并到一起变成一个/lib目录,这个目录里的类库相当于以前/common目录中类库的作用。这是Tomcat设计团队为了简化大多数的部署场景所做的一项改进,如果默认设置不能满足需要,用户可以通过修改配置文件指定server.loadershare.loader的方式重新启用Tomcat 5.x的加载器架构。
        Tomcat加载器的实现清晰易懂,并且采用了官方推荐的正统的使用类加载器的方式。如果读者阅读完上面的案例后,能完全理解Tomcat设计团队这样布置加载器架构的用意,那说明已经大致掌握了 类加载器主流的使用方式,那么笔者不妨再提一个问题让读者思考一下:前面曾经提到过一个场景,如果有10Web应用程序都是用Spring来进行组织和管理的话,可以把Spring放到CommonShared目录下让这些程序共享。Spring要对用户程序的类进行管理,自然要能访问到用户程序的类,而用户的程序显然是放在/WebApp/WEB-INF目录中的,那么被CommonClassLoaderSharedClassLoader加载的Spring如何访问并不在其加载范围内的用户程序呢?
        答案:
    线程上下文类加载器,可以实现父加载器对子加载器的逆向访问。

    本文为风林火山博客原创,转载请注明出处:www.flcoder.com