Tomcat7.0源码分析——类加载体系

目录(?)[+]

前言

Tomcat遵循J2EE规范,实现了Web容器。很多有关web的书籍和文章都离不开对Tomcat的分析,初学者可以从Tomcat的实现对J2EE有更深入的了解。此外,Tomcat还根据Java虚拟机规范实现了经典的双亲委派模式的类加载体系。本文基于Tomcat7.0的Java源码,对其类加载体系进行分析。

概述

首先简单介绍下Java虚拟机规范中提到的主要类加载器;
  • Bootstrap Loader:加载lib目录下或者System.getProperty(“sun.boot.class.path”)、或者-XBootclasspath所指定的路径或jar。
  • Extended Loader:加载lib\ext目录下或者System.getProperty(“java.ext.dirs”) 所指定的 路径或jar。在使用Java运行程序时,也可以指定其搜索路径,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld。
  • AppClass Loader:加载System.getProperty("java.class.path")所指定的 路径或jar。在使用Java运行程序时,也可以加上-cp来覆盖原有的Classpath设置,例如: java -cp ./lavasoft/classes HelloWorld。
然后用一张图片来展示Tomcat的类加载体系:

这里对上图所示类加载体系进行介绍:
ClassLoader:Java提供的类加载器抽象类,用户自定义的类加载器需要继承实现
commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;

源码分析

commonLoader、catalinaLoader和sharedLoader在Tomcat容器初始化的一开始,即调用Bootstrap的init方法时创建。catalinaLoader会被设置为Tomcat主线程的线程上下文类加载器,并且使用catalinaLoader加载Tomcat容器自身容器下的class。Bootstrap的init方法的部分代码清单如下:

  1. /** 
  2.  * Initialize daemon. 
  3.  */  
  4. public void init()  
  5.     throws Exception  
  6. {  
  7.   
  8.     // Set Catalina path  
  9.     setCatalinaHome();  
  10.     setCatalinaBase();  
  11.   
  12.     initClassLoaders();  
  13.   
  14.     Thread.currentThread().setContextClassLoader(catalinaLoader);  
  15.   
  16.     SecurityClassLoad.securityClassLoad(catalinaLoader);  
  17.     // 省略后边的代码  

 我们接着来看initClassLoaders方法的实现:

  1. private void initClassLoaders() {  
  2.     try {  
  3.         commonLoader = createClassLoader("common"null);  
  4.         if( commonLoader == null ) {  
  5.             // no config file, default to this loader - we might be in a 'single' env.  
  6.             commonLoader=this.getClass().getClassLoader();  
  7.         }  
  8.         catalinaLoader = createClassLoader("server", commonLoader);  
  9.         sharedLoader = createClassLoader("shared", commonLoader);  
  10.     } catch (Throwable t) {  
  11.         log.error("Class loader creation threw exception", t);  
  12.         System.exit(1);  
  13.     }  
  14. }  

 创建类加载器的createClassLoader方法的实现:

  1. private ClassLoader createClassLoader(String name, ClassLoader parent)  
  2.     throws Exception {  
  3.   
  4.     String value = CatalinaProperties.getProperty(name + ".loader");  
  5.     if ((value == null) || (value.equals("")))  
  6.         return parent;  
  7.   
  8.     ArrayList<String> repositoryLocations = new ArrayList<String>();  
  9.     ArrayList<Integer> repositoryTypes = new ArrayList<Integer>();  
  10.     int i;  
  11.   
  12.     StringTokenizer tokenizer = new StringTokenizer(value, ",");  
  13.     while (tokenizer.hasMoreElements()) {  
  14.         String repository = tokenizer.nextToken();  
  15.   
  16.         // Local repository  
  17.         boolean replace = false;  
  18.         String before = repository;  
  19.         while ((i=repository.indexOf(CATALINA_HOME_TOKEN))>=0) {  
  20.             replace=true;  
  21.             if (i>0) {  
  22.             repository = repository.substring(0,i) + getCatalinaHome()   
  23.                 + repository.substring(i+CATALINA_HOME_TOKEN.length());  
  24.             } else {  
  25.                 repository = getCatalinaHome()   
  26.                     + repository.substring(CATALINA_HOME_TOKEN.length());  
  27.             }  
  28.         }  
  29.         while ((i=repository.indexOf(CATALINA_BASE_TOKEN))>=0) {  
  30.             replace=true;  
  31.             if (i>0) {  
  32.             repository = repository.substring(0,i) + getCatalinaBase()   
  33.                 + repository.substring(i+CATALINA_BASE_TOKEN.length());  
  34.             } else {  
  35.                 repository = getCatalinaBase()   
  36.                     + repository.substring(CATALINA_BASE_TOKEN.length());  
  37.             }  
  38.         }  
  39.         if (replace && log.isDebugEnabled())  
  40.             log.debug("Expanded " + before + " to " + repository);  
  41.   
  42.         // Check for a JAR URL repository  
  43.         try {  
  44.             new URL(repository);  
  45.             repositoryLocations.add(repository);  
  46.             repositoryTypes.add(ClassLoaderFactory.IS_URL);  
  47.             continue;  
  48.         } catch (MalformedURLException e) {  
  49.             // Ignore  
  50.         }  
  51.   
  52.         if (repository.endsWith("*.jar")) {  
  53.             repository = repository.substring  
  54.                 (0, repository.length() - "*.jar".length());  
  55.             repositoryLocations.add(repository);  
  56.             repositoryTypes.add(ClassLoaderFactory.IS_GLOB);  
  57.         } else if (repository.endsWith(".jar")) {  
  58.             repositoryLocations.add(repository);  
  59.             repositoryTypes.add(ClassLoaderFactory.IS_JAR);  
  60.         } else {  
  61.             repositoryLocations.add(repository);  
  62.             repositoryTypes.add(ClassLoaderFactory.IS_DIR);  
  63.         }  
  64.     }  
  65.   
  66.     String[] locations = repositoryLocations.toArray(new String[0]);  
  67.     Integer[] types = repositoryTypes.toArray(new Integer[0]);  
  68.   
  69.     ClassLoader classLoader = ClassLoaderFactory.createClassLoader  
  70.         (locations, types, parent);  
  71.   
  72.     // Retrieving MBean server  
  73.     MBeanServer mBeanServer = null;  
  74.     if (MBeanServerFactory.findMBeanServer(null).size() > 0) {  
  75.         mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0);  
  76.     } else {  
  77.         mBeanServer = ManagementFactory.getPlatformMBeanServer();  
  78.     }  
  79.   
  80.     // Register the server classloader  
  81.     ObjectName objectName =  
  82.         new ObjectName("Catalina:type=ServerClassLoader,name=" + name);  
  83.     mBeanServer.registerMBean(classLoader, objectName);  
  84.   
  85.     return classLoader;  
  86.   
  87. }  

createClassLoader最终使用ClassLoaderFactory.createClassLoader(locations, types, parent)方法创建ClassLoader。我们回头看看SecurityClassLoad.securityClassLoad(catalinaLoader)的实现:

  1. public static void securityClassLoad(ClassLoader loader)  
  2.     throws Exception {  
  3.   
  4.     if( System.getSecurityManager() == null ){  
  5.         return;  
  6.     }  
  7.       
  8.     loadCorePackage(loader);  
  9.     loadLoaderPackage(loader);  
  10.     loadSessionPackage(loader);  
  11.     loadUtilPackage(loader);  
  12.     loadJavaxPackage(loader);  
  13.     loadCoyotePackage(loader);          
  14.     loadTomcatPackage(loader);  
  15. }  

securityClassLoad方法主要加载Tomcat容器所需的class,包括:

  • Tomcat核心class,即org.apache.catalina.core路径下的class;
  • org.apache.catalina.loader.WebappClassLoader$PrivilegedFindResourceByName;
  • Tomcat有关session的class,即org.apache.catalina.session路径下的class;
  • Tomcat工具类的class,即org.apache.catalina.util路径下的class;
  • javax.servlet.http.Cookie;
  • Tomcat处理请求的class,即org.apache.catalina.connector路径下的class;
  • Tomcat其它工具类的class,也是org.apache.catalina.util路径下的class;

我们以加载Tomcat核心class的loadCorePackage方法为例,查看其实现:

  1. private final static void loadCorePackage(ClassLoader loader)  
  2.     throws Exception {  
  3.     String basePackage = "org.apache.catalina.";  
  4.     loader.loadClass  
  5.         (basePackage +  
  6.          "core.ApplicationContextFacade$1");  
  7.     loader.loadClass  
  8.         (basePackage +  
  9.          "core.ApplicationDispatcher$PrivilegedForward");  
  10.     loader.loadClass  
  11.         (basePackage +  
  12.          "core.ApplicationDispatcher$PrivilegedInclude");  
  13.     loader.loadClass  
  14.         (basePackage +  
  15.         "core.AsyncContextImpl");  
  16.     loader.loadClass  
  17.         (basePackage +  
  18.         "core.AsyncContextImpl$AsyncState");  
  19.     loader.loadClass  
  20.         (basePackage +  
  21.         "core.AsyncContextImpl$DebugException");  
  22.     loader.loadClass  
  23.         (basePackage +  
  24.         "core.AsyncContextImpl$1");  
  25.     loader.loadClass  
  26.         (basePackage +  
  27.         "core.AsyncContextImpl$2");  
  28.     loader.loadClass  
  29.         (basePackage +  
  30.         "core.AsyncListenerWrapper");  
  31.     loader.loadClass  
  32.         (basePackage +  
  33.          "core.ContainerBase$PrivilegedAddChild");  
  34.     loader.loadClass  
  35.         (basePackage +  
  36.          "core.DefaultInstanceManager$1");  
  37.     loader.loadClass  
  38.         (basePackage +  
  39.          "core.DefaultInstanceManager$2");  
  40.     loader.loadClass  
  41.         (basePackage +  
  42.          "core.DefaultInstanceManager$3");  
  43.     loader.loadClass  
  44.         (basePackage +  
  45.          "core.DefaultInstanceManager$4");  
  46.     loader.loadClass  
  47.         (basePackage +  
  48.          "core.DefaultInstanceManager$5");  
  49.     loader.loadClass  
  50.         (basePackage +  
  51.          "core.ApplicationHttpRequest$AttributeNamesEnumerator");  
  52. }  

 至此,我们还没有看到WebappClassLoader。启动StandardContext的时候会创建WebappLoader,StandardContext的方法startInternal的部分代码如下:

  1. /** 
  2.  * Start this component and implement the requirements 
  3.  * of {@link LifecycleBase#startInternal()}. 
  4.  * 
  5.  * @exception LifecycleException if this component detects a fatal error 
  6.  *  that prevents this component from being used 
  7.  */  
  8. @Override  
  9. protected synchronized void startInternal() throws LifecycleException {  
  10.   
  11.     // 省略前边的代码   
  12.   
  13.     if (getLoader() == null) {  
  14.         WebappLoader webappLoader = new WebappLoader(getParentClassLoader());  
  15.         webappLoader.setDelegate(getDelegate());  
  16.         setLoader(webappLoader);  
  17.     }  
  18.    // 省略中间的代码   
  19.    // Start our subordinate components, if any  
  20.    if ((loader != null) && (loader instanceof Lifecycle))  
  21.         ((Lifecycle) loader).start();   
  22.    // 省略后边的代码   
  23. }  

 从上面代码看到最后会调用WebappLoader的start方法,start又调用了startInternal方法,startInternal的实现如下:

  1. /** 
  2.  * Start associated {@link ClassLoader} and implement the requirements 
  3.  * of {@link LifecycleBase#startInternal()}. 
  4.  * 
  5.  * @exception LifecycleException if this component detects a fatal error 
  6.  *  that prevents this component from being used 
  7.  */  
  8. @Override  
  9. protected void startInternal() throws LifecycleException {  
  10.       
  11.     // Register a stream handler factory for the JNDI protocol  
  12.     URLStreamHandlerFactory streamHandlerFactory =  
  13.         new DirContextURLStreamHandlerFactory();  
  14.     if (first) {  
  15.         first = false;  
  16.         try {  
  17.             URL.setURLStreamHandlerFactory(streamHandlerFactory);  
  18.         } catch (Exception e) {  
  19.             // Log and continue anyway, this is not critical  
  20.             log.error("Error registering jndi stream handler", e);  
  21.         } catch (Throwable t) {  
  22.             // This is likely a dual registration  
  23.             log.info("Dual registration of jndi stream handler: "   
  24.                      + t.getMessage());  
  25.         }  
  26.     }  
  27.   
  28.     // Construct a class loader based on our current repositories list  
  29.     try {  
  30.   
  31.         classLoader = createClassLoader();  
  32.         classLoader.setResources(container.getResources());  
  33.         classLoader.setDelegate(this.delegate);  
  34.         classLoader.setSearchExternalFirst(searchExternalFirst);  
  35.         if (container instanceof StandardContext) {  
  36.             classLoader.setAntiJARLocking(  
  37.                     ((StandardContext) container).getAntiJARLocking());  
  38.             classLoader.setClearReferencesStatic(  
  39.                     ((StandardContext) container).getClearReferencesStatic());  
  40.             classLoader.setClearReferencesStopThreads(  
  41.                     ((StandardContext) container).getClearReferencesStopThreads());  
  42.             classLoader.setClearReferencesStopTimerThreads(  
  43.                     ((StandardContext) container).getClearReferencesStopTimerThreads());  
  44.             classLoader.setClearReferencesThreadLocals(  
  45.                     ((StandardContext) container).getClearReferencesThreadLocals());  
  46.         }  
  47.   
  48.         for (int i = 0; i < repositories.length; i++) {  
  49.             classLoader.addRepository(repositories[i]);  
  50.         }  

 最后我们看看createClassLoader的实现:

  1. /** 
  2.  * Create associated classLoader. 
  3.  */  
  4. private WebappClassLoader createClassLoader()  
  5.     throws Exception {  
  6.   
  7.     //loaderClass即字符串org.apache.catalina.loader.WebappClassLoader  
  8.     Class<?> clazz = Class.forName(loaderClass);  
  9.     WebappClassLoader classLoader = null;  
  10.   
  11.     if (parentClassLoader == null) {  
  12.         parentClassLoader = container.getParentClassLoader();  
  13.     }  
  14.     Class<?>[] argTypes = { ClassLoader.class };  
  15.     Object[] args = { parentClassLoader };  
  16.     Constructor<?> constr = clazz.getConstructor(argTypes);  
  17.     classLoader = (WebappClassLoader) constr.newInstance(args);  
  18.   
  19.     return classLoader;  
  20.   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值