Tomcat类加载器体系结构

与Java语言相似,Tomcat提供了分级类加载机制,当然,最顶端非BootStrap Loader莫属,它由C++编写,JVM启动且完成初始化后首先被调用。不过在Tomcat中类加载体系中,缺少了ExtClassLoader的身影(待验证),ExtClassLoader加载sun公司提供的扩展机制(参考文章:<<Extension Mechanism Architecture>>)。下面是架构图: 

   
上图展示了各层类加载器以及类文件搜索路径,Tomcat为每个部署到其中的Web项目定义一个类加载器(如上图中WebappAClassLoader、WebappBClassLoader),其类文件搜索路径即为%CATALINA_HOME%\webapps\项目名称\WEB-INF\lib\;
%CATALINA_HOME%\webapps\项目名称\WEB-INF\classes\。Tomcat自己定义了一个BootStrap类,在org.apache.catalina.startup.BootStrap定义,其作用是:
1、定义公共类加载器
Java代码 复制代码
  1. private Object catalinaDaemon = null;//定义catalina服务器守护程序实例   
  2.   
  3. protected ClassLoader commonLoader = null;   
  4. protected ClassLoader catalinaLoader = null;   
  5. protected ClassLoader sharedLoader = null;   
  6.   
  7. private void initClassLoaders() {   
  8.     try {   
  9.         ClassLoaderFactory.setDebug(debug);   
  10.         commonLoader = createClassLoader("common"null);   
  11.         catalinaLoader = createClassLoader("server", commonLoader);   
  12.         sharedLoader = createClassLoader("shared", commonLoader);   
  13.     } catch (Throwable t) {   
  14.         log("Class loader creation threw exception", t);   
  15.         System.exit(1);   
  16.     }   
  17. }  

我们看到commonLoader的父类加载器为null,即在委派机制下它将把类加载任务直接委派给JVM所使用的BootStrap Loader,但为什么是null呢?因为JVM所使用的BootStrap Loader是用C++编写的。
catalinaDaemon为服务器从启动至停止都存在的守护线程,类似于桌面程序中的Main守护线程。createClassLoader函数利用ClassLoaderFactory类在工厂模式下创建,创建代码如下:
Java代码 复制代码
  1. public static ClassLoader createClassLoader(File unpacked[],File packed[], URL urls[], ClassLoader parent)throws Exception {   
  2. ..   
  3.        //获得将要创建的类加载器的类文件搜索路径   
  4.        String array[] = (String[]) list.toArray(new String[list.size()]);   
  5.        StandardClassLoader classLoader = null;   
  6.        if (parent == null)//父加载器为JVM使用的BootStrap Loader   
  7.            classLoader = new StandardClassLoader(array);   
  8.        else  
  9.            classLoader = new StandardClassLoader(array, parent);   
  10.        classLoader.setDelegate(true);//设置该类加载器遵循委派模式   
  11.        return (classLoader);   

Tomcat提供两种类加载器供使用,一种是如上代码中所述的标准类加载器StandardClassLoader,用以实例化为commonLoader、catalinaLoader和sharedLoader,在org.apache.catalina.loader.StandardClassLoader定义,它不提供热部署功能;另外一种是专为Web程序所提供的WebClassLoader,它用以实例化为各部署项目的类加载器,在org.apache.catalina.loader.WebappClassLoader定义,提供热部署功能,也就是在发生ClassLoader搜索路径下的资源改变的动作之后,服务器自动重新加载之。
2、初始化catalina守护程序:
Java代码 复制代码
  1. public void init()   
  2.     throws Exception   
  3. {   
  4.     // Set Catalina path   
  5.     setCatalinaHome();   
  6.     setCatalinaBase();   
  7.        
  8.     initClassLoaders();   
  9.     Thread.currentThread().setContextClassLoader(catalinaLoader);   
  10.     SecurityClassLoad.securityClassLoad(catalinaLoader);   
  11.   
  12.     /*利用类加载器catalinaClassLoader加载Catalina,并调用后者的process方法,该方法设置%CATALINA_HOME%,%CATALINA_BASE%,并根据参数配置启动catalina*/  
  13.     Class startupClass =   
  14.         catalinaLoader.loadClass   
  15.         ("org.apache.catalina.startup.Catalina");   
  16.     Object startupInstance = startupClass.newInstance();   
  17.        
  18.     /*将SharedClassLoader设为Catalina类的ClassLoader*/  
  19.     String methodName = "setParentClassLoader";   
  20.     Class paramTypes[] = new Class[1];   
  21.     paramTypes[0] = Class.forName("java.lang.ClassLoader");   
  22.     Object paramValues[] = new Object[1];   
  23.     paramValues[0] = sharedLoader;   
  24.     Method method =   
  25.         startupInstance.getClass().getMethod(methodName, paramTypes);   
  26.            
  27.     method.invoke(startupInstance, paramValues);   
  28.     catalinaDaemon = startupInstance;   
  29. }  

本段代码实现了利用CatalinaClassLoader加载Catalina类,并创建其实例。但很有意思的是,创建实例之后Catalina的类加载器却被设置为SharedClassLoader。
我们知道Catalina是Tomcat容器的代言人,也就是一个在容器生命周期内都存在的类,我们所设计的Servlet是被放置在这个容器里面供调用的,从代码层来讲也就是被实例化,然后引用。同时,在Java类加载器体系结构中定义到:被引用类默认由依赖类的ClassLoader加载,而这样设计的原因是,运行时相同层次的ClassLoader所加载的类无法看到其他ClassLoader所加载的类,可这又是为什么呢?这是Java语言的安全特性所要求的(进一步探讨,请参阅《深入Java虚拟机》)。由上所述,可以知道如果要引用Servlet的话,得有Catalina的ClassLoader出马去加载,但是我们之前已经看到了,每一个Web项目都有一个特定的WebappClassLoder加载,并且Catalina需要引用的可是同时部署到其中的许多个Web项目的Servlet,这就出现了矛盾。但是,我们仔细看看WebappClassLoader的设计,它的父加载器可是SharedClassLoader哦(SharedClassLoader加载的是部署到容器中的多个Web项目共用的资源),故聪明的小花猫把Catalina的类加载器设置为了SharedClassLoader,这样利用父加载器加载Catalina,而子加载器来加载Servlet,岂不是一个绝妙的设计!
注意:代码中利用到了强大的反射机制。

延伸探究:
http://blog.chinaunix.net/u2/83532/showart_1418390.html
http://blog.163.com/haizai219@126/blog/static/44412555200810111429791/
posted @ 2010-07-31 23:08  Ivan-zheng  阅读(1575)  评论(0编辑  收藏  举报