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、定义公共类加载器
我们看到commonLoader的父类加载器为null,即在委派机制下它将把类加载任务直接委派给JVM所使用的BootStrap Loader,但为什么是null呢?因为JVM所使用的BootStrap Loader是用C++编写的。
catalinaDaemon为服务器从启动至停止都存在的守护线程,类似于桌面程序中的Main守护线程。createClassLoader函数利用ClassLoaderFactory类在工厂模式下创建,创建代码如下:
Tomcat提供两种类加载器供使用,一种是如上代码中所述的标准类加载器StandardClassLoader,用以实例化为commonLoader、catalinaLoader和sharedLoader,在org.apache.catalina.loader.StandardClassLoader定义,它不提供热部署功能;另外一种是专为Web程序所提供的WebClassLoader,它用以实例化为各部署项目的类加载器,在org.apache.catalina.loader.WebappClassLoader定义,提供热部署功能,也就是在发生ClassLoader搜索路径下的资源改变的动作之后,服务器自动重新加载之。
2、初始化catalina守护程序:
本段代码实现了利用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/
上图展示了各层类加载器以及类文件搜索路径,Tomcat为每个部署到其中的Web项目定义一个类加载器(如上图中WebappAClassLoader、WebappBClassLoader),其类文件搜索路径即为%CATALINA_HOME%\webapps\项目名称\WEB-INF\lib\;
%CATALINA_HOME%\webapps\项目名称\WEB-INF\classes\。Tomcat自己定义了一个BootStrap类,在org.apache.catalina.startup.BootStrap定义,其作用是:
1、定义公共类加载器
- private Object catalinaDaemon = null;//定义catalina服务器守护程序实例
- protected ClassLoader commonLoader = null;
- protected ClassLoader catalinaLoader = null;
- protected ClassLoader sharedLoader = null;
- private void initClassLoaders() {
- try {
- ClassLoaderFactory.setDebug(debug);
- commonLoader = createClassLoader("common", null);
- catalinaLoader = createClassLoader("server", commonLoader);
- sharedLoader = createClassLoader("shared", commonLoader);
- } catch (Throwable t) {
- log("Class loader creation threw exception", t);
- System.exit(1);
- }
- }
我们看到commonLoader的父类加载器为null,即在委派机制下它将把类加载任务直接委派给JVM所使用的BootStrap Loader,但为什么是null呢?因为JVM所使用的BootStrap Loader是用C++编写的。
catalinaDaemon为服务器从启动至停止都存在的守护线程,类似于桌面程序中的Main守护线程。createClassLoader函数利用ClassLoaderFactory类在工厂模式下创建,创建代码如下:
- public static ClassLoader createClassLoader(File unpacked[],File packed[], URL urls[], ClassLoader parent)throws Exception {
- ..
- //获得将要创建的类加载器的类文件搜索路径
- String array[] = (String[]) list.toArray(new String[list.size()]);
- StandardClassLoader classLoader = null;
- if (parent == null)//父加载器为JVM使用的BootStrap Loader
- classLoader = new StandardClassLoader(array);
- else
- classLoader = new StandardClassLoader(array, parent);
- classLoader.setDelegate(true);//设置该类加载器遵循委派模式
- return (classLoader);
Tomcat提供两种类加载器供使用,一种是如上代码中所述的标准类加载器StandardClassLoader,用以实例化为commonLoader、catalinaLoader和sharedLoader,在org.apache.catalina.loader.StandardClassLoader定义,它不提供热部署功能;另外一种是专为Web程序所提供的WebClassLoader,它用以实例化为各部署项目的类加载器,在org.apache.catalina.loader.WebappClassLoader定义,提供热部署功能,也就是在发生ClassLoader搜索路径下的资源改变的动作之后,服务器自动重新加载之。
2、初始化catalina守护程序:
- public void init()
- throws Exception
- {
- // Set Catalina path
- setCatalinaHome();
- setCatalinaBase();
- initClassLoaders();
- Thread.currentThread().setContextClassLoader(catalinaLoader);
- SecurityClassLoad.securityClassLoad(catalinaLoader);
- /*利用类加载器catalinaClassLoader加载Catalina,并调用后者的process方法,该方法设置%CATALINA_HOME%,%CATALINA_BASE%,并根据参数配置启动catalina*/
- Class startupClass =
- catalinaLoader.loadClass
- ("org.apache.catalina.startup.Catalina");
- Object startupInstance = startupClass.newInstance();
- /*将SharedClassLoader设为Catalina类的ClassLoader*/
- String methodName = "setParentClassLoader";
- Class paramTypes[] = new Class[1];
- paramTypes[0] = Class.forName("java.lang.ClassLoader");
- Object paramValues[] = new Object[1];
- paramValues[0] = sharedLoader;
- Method method =
- startupInstance.getClass().getMethod(methodName, paramTypes);
- method.invoke(startupInstance, paramValues);
- catalinaDaemon = startupInstance;
- }
本段代码实现了利用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/