tomcat解析之类加载器

OK, 现在进入正题. Tomcat类加载器初始化.

开始之前,我们首先需要了解一下几个基本的知识点;

1.tomcat中类加载器的结构与关系。

   这里,我引用tomcat文档的一个简图来说明一下, 有兴趣深究的朋友,可以去翻看tomcat的文档,理解更多信息.

  


(tomcat5.5)

由于参考的是tomcat6.0的源代码,这里纠正一下类加载器的图(如下),以免给别的朋友造成误解,同时也多谢asialee给出的提醒


(tomcat6.0)

2.每种类加载器分别加载什么资源: 

     这些内容,可以在tomcat文档的  Class Loader HOW-TO 找到.

    这里我要说明的是, 在tomcat中,这些内容是记录在哪里的。既(程序怎么让tomcat知道,需要加载哪些类)

    答案是-----

     其通过一个配置文件来指定的:(catalina.properties),这个文件默认存放在

    tomcat路径下的 bin/bootstrap.jar中。

    如图

    

  
    打开文件,其内容如下:

Java代码 复制代码 收藏代码
  1. # Licensed to the Apache Software Foundation (ASF) under one or more 
  2. # contributor license agreements.  See the NOTICE file distributed with 
  3. # this work for additional information regarding copyright ownership. 
  4. # The ASF licenses this file to You under the Apache License, Version2.0 
  5. # (the "License"); you may not use this file except in compliance with 
  6. # the License.  You may obtain a copy of the License at 
  7. #     http://www.apache.org/licenses/LICENSE-2.0 
  8. # Unless required by applicable law or agreed to in writing, software 
  9. # distributed under the License is distributed on an "AS IS" BASIS, 
  10. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  11. # See the License for the specific language governing permissions and 
  12. # limitations under the License. 
  13.  
  14. # List of comma-separated packages that start with or equal this string 
  15. # will cause a security exception to be thrown when 
  16. # passed to checkPackageAccess unless the 
  17. # corresponding RuntimePermission ("accessClassInPackage."+package) has 
  18. # been granted. 
  19. package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.,sun.beans. 
  20. # List of comma-separated packages that start with or equal this string 
  21. # will cause a security exception to be thrown when 
  22. # passed to checkPackageDefinition unless the 
  23. # corresponding RuntimePermission ("defineClassInPackage."+package) has 
  24. # been granted. 
  25. # by default, no packages are restrictedfor definition, and none of 
  26. # the class loaders supplied with the JDK call checkPackageDefinition. 
  27. package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper. 
  28.  
  29. # List of comma-separated paths defining the contents of the "common"  
  30. # classloader. Prefixes should be used to define what is the repository type. 
  31. # Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute. 
  32. # If left as blank,the JVM system loader will be used as Catalina's "common"  
  33. # loader. 
  34. # Examples: 
  35. #     "foo": Add this folder as aclass repository 
  36. #     "foo/*.jar": Add all the JARs of the specified folder asclass  
  37. #                  repositories 
  38. #     "foo/bar.jar": Add bar.jar as aclass repository 
  39. common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar 
  40.  
  41. # List of comma-separated paths defining the contents of the "server"  
  42. # classloader. Prefixes should be used to define what is the repository type. 
  43. # Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute. 
  44. # If left as blank, the "common" loader will be used as Catalina's"server"  
  45. # loader. 
  46. # Examples: 
  47. #     "foo": Add this folder as aclass repository 
  48. #     "foo/*.jar": Add all the JARs of the specified folder asclass  
  49. #                  repositories 
  50. #     "foo/bar.jar": Add bar.jar as aclass repository 
  51. server.loader= 
  52.  
  53. # List of comma-separated paths defining the contents of the "shared"  
  54. # classloader. Prefixes should be used to define what is the repository type. 
  55. # Path may be relative to the CATALINA_BASE path or absolute. If left as blank, 
  56. # the "common" loader will be used as Catalina's"shared" loader. 
  57. # Examples: 
  58. #     "foo": Add this folder as aclass repository 
  59. #     "foo/*.jar": Add all the JARs of the specified folder asclass  
  60. #                  repositories 
  61. #     "foo/bar.jar": Add bar.jar as aclass repository  
  62. # Please note that for single jars, e.g. bar.jar, you need the URL form 
  63. # starting with file:. 
  64. shared.loader= 
  65.  
  66. # String cache configuration. 
  67. tomcat.util.buf.StringCache.byte.enabled=true 
  68. #tomcat.util.buf.StringCache.char.enabled=true 
  69. #tomcat.util.buf.StringCache.trainThreshold=500000 
  70. #tomcat.util.buf.StringCache.cacheSize=5000 
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

#
# List of comma-separated packages that start with or equal this string
# will cause a security exception to be thrown when
# passed to checkPackageAccess unless the
# corresponding RuntimePermission ("accessClassInPackage."+package) has
# been granted.
package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.,sun.beans.
#
# List of comma-separated packages that start with or equal this string
# will cause a security exception to be thrown when
# passed to checkPackageDefinition unless the
# corresponding RuntimePermission ("defineClassInPackage."+package) has
# been granted.
#
# by default, no packages are restricted for definition, and none of
# the class loaders supplied with the JDK call checkPackageDefinition.
#
package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.

#
#
# List of comma-separated paths defining the contents of the "common" 
# classloader. Prefixes should be used to define what is the repository type.
# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
# If left as blank,the JVM system loader will be used as Catalina's "common" 
# loader.
# Examples:
#     "foo": Add this folder as a class repository
#     "foo/*.jar": Add all the JARs of the specified folder as class 
#                  repositories
#     "foo/bar.jar": Add bar.jar as a class repository
common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar

#
# List of comma-separated paths defining the contents of the "server" 
# classloader. Prefixes should be used to define what is the repository type.
# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
# If left as blank, the "common" loader will be used as Catalina's "server" 
# loader.
# Examples:
#     "foo": Add this folder as a class repository
#     "foo/*.jar": Add all the JARs of the specified folder as class 
#                  repositories
#     "foo/bar.jar": Add bar.jar as a class repository
server.loader=

#
# List of comma-separated paths defining the contents of the "shared" 
# classloader. Prefixes should be used to define what is the repository type.
# Path may be relative to the CATALINA_BASE path or absolute. If left as blank,
# the "common" loader will be used as Catalina's "shared" loader.
# Examples:
#     "foo": Add this folder as a class repository
#     "foo/*.jar": Add all the JARs of the specified folder as class 
#                  repositories
#     "foo/bar.jar": Add bar.jar as a class repository 
# Please note that for single jars, e.g. bar.jar, you need the URL form
# starting with file:.
shared.loader=

#
# String cache configuration.
tomcat.util.buf.StringCache.byte.enabled=true
#tomcat.util.buf.StringCache.char.enabled=true
#tomcat.util.buf.StringCache.trainThreshold=500000
#tomcat.util.buf.StringCache.cacheSize=5000

此文件,下面会有详细的介绍.

OK,到此,我们初步了解到tomcat关于类加载器的一些知识。 下面来详细看看,tomcat内部是怎么来初始化这些类加载器的吧.

首先, 我们知道, java程序都需要一个入口(main方法), 而在tomcat中,这个入口在

  org.apache.catalina.startup.Bootstrap 这个类中。

  看其结构:

 


    定位到方法内部:

    

Java代码 复制代码 收藏代码
  1. public staticvoid main(String args[]) { 
  2.  
  3.         if (daemon == null) { 
  4.             daemon = new Bootstrap(); 
  5.             try
  6.                 //初始化资源   (今天来了解的.) 
  7.                 daemon.init(); 
  8.             } catch (Throwable t) { 
  9.                 t.printStackTrace(); 
  10.                 return
  11.             } 
  12.         } 
  13.          
  14.         try
  15.             //默认为启动 
  16.             String command = "start"
  17.             if (args.length > 0) { 
  18.                 command = args[args.length - 1]; 
  19.             } 
  20.              
  21.             if (command.equals("startd")) { 
  22.                 args[args.length - 1] ="start"
  23.                 daemon.load(args); 
  24.                 daemon.start(); 
  25.             } else if (command.equals("stopd")) { 
  26.                 args[args.length - 1] ="stop"
  27.                 daemon.stop(); 
  28.             } else if (command.equals("start")) { 
  29.                 //设置标识 
  30.                 daemon.setAwait(true); 
  31.                 daemon.load(args); 
  32.                 //开启 
  33.                 daemon.start(); 
  34.             } else if (command.equals("stop")) { 
  35.                 daemon.stopServer(args); 
  36.             } else
  37.                 log.warn("Bootstrap: command \"" + command +"\" does not exist."); 
  38.             } 
  39.         } catch (Throwable t) { 
  40.             t.printStackTrace(); 
  41.         } 
  42.     } 
public static void main(String args[]) {

        if (daemon == null) {
            daemon = new Bootstrap();
            try {
            	//初始化资源   (今天来了解的.)
                daemon.init();
            } catch (Throwable t) {
                t.printStackTrace();
                return;
            }
        }
        
        try {
        	//默认为启动
            String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }
            
            if (command.equals("startd")) {
                args[args.length - 1] = "start";
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stopd")) {
                args[args.length - 1] = "stop";
                daemon.stop();
            } else if (command.equals("start")) {
            	//设置标识
                daemon.setAwait(true);
                daemon.load(args);
                //开启
                daemon.start();
            } else if (command.equals("stop")) {
                daemon.stopServer(args);
            } else {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

 

  在tomcat启动之前, 需要初始化一些系统资源, 初始化的详细工作都定义在init()方法内部了。

    OK,我们继续追踪一下。 定位到init()方法中.\

Java代码 复制代码 收藏代码
  1. public void init() 
  2.     throws Exception 
  3.     // Set Catalina path 设置catalina基本路径 
  4.     setCatalinaHome(); 
  5.     setCatalinaBase(); 
  6.      
  7.     //初始化类加载器 
  8.     initClassLoaders(); 
  9.      
  10.     Thread.currentThread().setContextClassLoader(catalinaLoader); 
  11.     SecurityClassLoad.securityClassLoad(catalinaLoader); 
  12.     // Load our startup class and call its process() method 
  13.     if (log.isDebugEnabled()) 
  14.         log.debug("Loading startup class"); 
  15.     Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina"); 
  16.     Object startupInstance = startupClass.newInstance(); 
  17.     // Set the shared extensions class loader 
  18.     if (log.isDebugEnabled()) 
  19.         log.debug("Setting startup class properties"); 
  20.     String methodName = "setParentClassLoader"
  21.     Class paramTypes[] = new Class[1]; 
  22.     paramTypes[0] = Class.forName("java.lang.ClassLoader"); 
  23.     Object paramValues[] = new Object[1]; 
  24.     paramValues[0] = sharedLoader; 
  25.     Method method =startupInstance.getClass().getMethod(methodName, paramTypes); 
  26.     method.invoke(startupInstance, paramValues); 
  27.     catalinaDaemon = startupInstance; 
    public void init()
        throws Exception
    {
        // Set Catalina path 设置catalina基本路径
        setCatalinaHome();
        setCatalinaBase();
        
        //初始化类加载器
        initClassLoaders();
        
        Thread.currentThread().setContextClassLoader(catalinaLoader);
        SecurityClassLoad.securityClassLoad(catalinaLoader);
        // Load our startup class and call its process() method
        if (log.isDebugEnabled())
            log.debug("Loading startup class");
        Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.newInstance();
        // Set the shared extensions class loader
        if (log.isDebugEnabled())
            log.debug("Setting startup class properties");
        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;
    }

  可以看到, 上面的代码中,用来初始化类加载器、验证类加载器、

  以及使用类加载器来加载类"org.apache.catalina.startup.Catalina"等等操作。

  这篇文章,主要来探讨一下,tomcat初始化类加载器的方式, ,所以,我们追踪到方法initClassLoaders()中:

 

   这里主要介绍一下,下面的流程, tomcat会调用initClassLoaders()方法。

   用来初始化common ,catalina,shared三种类加载器,而这个操作是通过方法

   createClassLoader(String name, ClassLoader parent)来完成的。

   而后2个都属于common的子级,

   所以下面给出2个方法的源代码(其中相关信息,都以注释给出):

  

 

Java代码 复制代码 收藏代码
  1. /**
  2.     * 初始化类加载器:
  3.     * 加载三种:
  4.     *        common.
  5.     *        /           \
  6.     *  catalina   shared.
  7.     */ 
  8.    private void initClassLoaders() { 
  9.        try
  10.         //创建common类加载器 
  11.            commonLoader = createClassLoader("common",null); 
  12.            if( commonLoader == null ) { 
  13.                // no config file, default to this loader - we might be in a 'single' env. 
  14.                commonLoader=this.getClass().getClassLoader(); 
  15.            } 
  16.            //创建catalina类加载器,指定其父级别的加载器为commonLoader. 
  17.            catalinaLoader = createClassLoader("server", commonLoader); 
  18.            //创建sharedLoader类加载器,指定其父级别的加载器为commonLoader. 
  19.            sharedLoader = createClassLoader("shared", commonLoader); 
  20.        } catch (Throwable t) { 
  21.            log.error("Class loader creation threw exception", t); 
  22.            System.exit(1); 
  23.        } 
  24.    } 
 /**
     * 初始化类加载器:
     * 加载三种:
     *        common.
     *        /           \
     *  catalina   shared. 
     */
    private void initClassLoaders() {
        try {
        	//创建common类加载器
            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();
            }
            //创建catalina类加载器,指定其父级别的加载器为commonLoader.
            catalinaLoader = createClassLoader("server", commonLoader);
            //创建sharedLoader类加载器,指定其父级别的加载器为commonLoader.
            sharedLoader = createClassLoader("shared", commonLoader);
        } catch (Throwable t) {
            log.error("Class loader creation threw exception", t);
            System.exit(1);
        }
    }

   

  

Java代码 复制代码 收藏代码
  1. /**
  2.      * 创建类加载器
  3.      *
  4.      * @param name
  5.      * @param parent 指定上一级别的类加载器
  6.      * @return
  7.      * @throws Exception
  8.      */ 
  9.     private ClassLoader createClassLoader(String name, ClassLoader parent) 
  10.         throws Exception { 
  11.         //这里以common为例: 从catalina.properties中获取common.loader  类加载信息 
  12.         //如: 
  13.         //common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar 
  14.         String value = CatalinaProperties.getProperty(name + ".loader"); 
  15.         // 如果没有任何信息,则返回父加载器 
  16.         if ((value == null) || (value.equals(""))) 
  17.             return parent; 
  18.          
  19.         ArrayList repositoryLocations = new ArrayList(); 
  20.         ArrayList repositoryTypes = new ArrayList(); 
  21.         int i; 
  22.         //以逗号分隔. 
  23.         StringTokenizer tokenizer = new StringTokenizer(value,","); 
  24.          
  25.         while (tokenizer.hasMoreElements()) { 
  26.             String repository = tokenizer.nextToken(); 
  27.             // Local repository 
  28.             boolean replace = false
  29.             String before = repository; 
  30.             //是否含有"${catalina.home}" 
  31.             while ((i=repository.indexOf(CATALINA_HOME_TOKEN))>=0) { 
  32.                 replace=true
  33.                 if (i>0) { 
  34.                     //替换成tomcat路径  替换后的形式如下: c:/opensource/tomcat5/lib. 
  35.                 repository = repository.substring(0,i) + getCatalinaHome()  
  36.                     + repository.substring(i+CATALINA_HOME_TOKEN.length()); 
  37.                 } else
  38.                     repository = getCatalinaHome()  
  39.                         + repository.substring(CATALINA_HOME_TOKEN.length()); 
  40.                 } 
  41.             } 
  42.             //是否含有"${catalina.base}" 
  43.             while ((i=repository.indexOf(CATALINA_BASE_TOKEN))>=0) { 
  44.                 replace=true
  45.                 if (i>0) { 
  46.                     //同上,替换  
  47.                 repository = repository.substring(0,i) + getCatalinaBase()  
  48.                     + repository.substring(i+CATALINA_BASE_TOKEN.length()); 
  49.                 } else
  50.                     repository = getCatalinaBase()  
  51.                         + repository.substring(CATALINA_BASE_TOKEN.length()); 
  52.                 } 
  53.             } 
  54.             if (replace && log.isDebugEnabled()) 
  55.                 log.debug("Expanded " + before +" to " + repository); 
  56.  
  57.             // Check for a JAR URL repository 
  58.             try
  59.                 URL url=new URL(repository); 
  60.                 repositoryLocations.add(repository); 
  61.                 repositoryTypes.add(ClassLoaderFactory.IS_URL); 
  62.                 continue
  63.             } catch (MalformedURLException e) { 
  64.                 // Ignore 
  65.             } 
  66.              
  67.              
  68.             if (repository.endsWith("*.jar")) { 
  69.                 repository = repository.substring 
  70.                     (0, repository.length() -"*.jar".length()); 
  71.                 repositoryLocations.add(repository); 
  72.                 repositoryTypes.add(ClassLoaderFactory.IS_GLOB); 
  73.             } else if (repository.endsWith(".jar")) { 
  74.                 repositoryLocations.add(repository); 
  75.                 repositoryTypes.add(ClassLoaderFactory.IS_JAR); 
  76.             } else
  77.                 repositoryLocations.add(repository); 
  78.                 repositoryTypes.add(ClassLoaderFactory.IS_DIR); 
  79.             } 
  80.         } 
  81.          
  82.          
  83.          
  84.         String[] locations = (String[]) repositoryLocations.toArray(new String[0]); 
  85.         Integer[] types = (Integer[]) repositoryTypes.toArray(new Integer[0]); 
  86.          
  87.         //创建类加载器 
  88.         ClassLoader classLoader = ClassLoaderFactory.createClassLoader 
  89.             (locations, types, parent); 
  90.  
  91.         // Retrieving MBean server 
  92.         MBeanServer mBeanServer = null
  93.         if (MBeanServerFactory.findMBeanServer(null).size() >0) { 
  94.             mBeanServer = 
  95.                 (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0); 
  96.         } else
  97.             mBeanServer = ManagementFactory.getPlatformMBeanServer(); 
  98.         } 
  99.  
  100.         // Register the server classloader 
  101.         ObjectName objectName = 
  102.             new ObjectName("Catalina:type=ServerClassLoader,name=" + name); 
  103.         mBeanServer.registerMBean(classLoader, objectName); 
  104.         return classLoader; 
  105.     } 
/**
     * 创建类加载器
     * 
     * @param name
     * @param parent 指定上一级别的类加载器
     * @return
     * @throws Exception
     */
    private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {
    	//这里以common为例: 从catalina.properties中获取common.loader  类加载信息 
    	//如:
    	//common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
        String value = CatalinaProperties.getProperty(name + ".loader");
        // 如果没有任何信息,则返回父加载器
        if ((value == null) || (value.equals("")))
            return parent;
        
        ArrayList repositoryLocations = new ArrayList();
        ArrayList repositoryTypes = new ArrayList();
        int i;
        //以逗号分隔.
        StringTokenizer tokenizer = new StringTokenizer(value, ",");
        
        while (tokenizer.hasMoreElements()) {
            String repository = tokenizer.nextToken();
            // Local repository
            boolean replace = false;
            String before = repository;
            //是否含有"${catalina.home}"
            while ((i=repository.indexOf(CATALINA_HOME_TOKEN))>=0) {
                replace=true;
                if (i>0) {
                	//替换成tomcat路径  替换后的形式如下: c:/opensource/tomcat5/lib.
                repository = repository.substring(0,i) + getCatalinaHome() 
                    + repository.substring(i+CATALINA_HOME_TOKEN.length());
                } else {
                    repository = getCatalinaHome() 
                        + repository.substring(CATALINA_HOME_TOKEN.length());
                }
            }
            //是否含有"${catalina.base}"
            while ((i=repository.indexOf(CATALINA_BASE_TOKEN))>=0) {
                replace=true;
                if (i>0) {
                	//同上,替换 
                repository = repository.substring(0,i) + getCatalinaBase() 
                    + repository.substring(i+CATALINA_BASE_TOKEN.length());
                } else {
                    repository = getCatalinaBase() 
                        + repository.substring(CATALINA_BASE_TOKEN.length());
                }
            }
            if (replace && log.isDebugEnabled())
                log.debug("Expanded " + before + " to " + repository);

            // Check for a JAR URL repository
            try {
                URL url=new URL(repository);
                repositoryLocations.add(repository);
                repositoryTypes.add(ClassLoaderFactory.IS_URL);
                continue;
            } catch (MalformedURLException e) {
                // Ignore
            }
            
            
            if (repository.endsWith("*.jar")) {
                repository = repository.substring
                    (0, repository.length() - "*.jar".length());
                repositoryLocations.add(repository);
                repositoryTypes.add(ClassLoaderFactory.IS_GLOB);
            } else if (repository.endsWith(".jar")) {
                repositoryLocations.add(repository);
                repositoryTypes.add(ClassLoaderFactory.IS_JAR);
            } else {
                repositoryLocations.add(repository);
                repositoryTypes.add(ClassLoaderFactory.IS_DIR);
            }
        }
        
        
        
        String[] locations = (String[]) repositoryLocations.toArray(new String[0]);
        Integer[] types = (Integer[]) repositoryTypes.toArray(new Integer[0]);
        
        //创建类加载器
        ClassLoader classLoader = ClassLoaderFactory.createClassLoader
            (locations, types, parent);

        // Retrieving MBean server
        MBeanServer mBeanServer = null;
        if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
            mBeanServer =
                (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0);
        } else {
            mBeanServer = ManagementFactory.getPlatformMBeanServer();
        }

        // Register the server classloader
        ObjectName objectName =
            new ObjectName("Catalina:type=ServerClassLoader,name=" + name);
        mBeanServer.registerMBean(classLoader, objectName);
        return classLoader;
    }

  到这里,我们可以确定,tomcat文档中的类加载器之间关系是准确的,并非凭空说的。 

    到这里,我们可能对于createClassLoader()方法的

Java代码 复制代码 收藏代码
  1. CatalinaProperties.getProperty(name + ".loader"); 
CatalinaProperties.getProperty(name + ".loader");

  有点疑问, 到底tomcat是如果通过配置文件来获取需要初始化类加载器的相关信息的呢/

 

前面我们看到 catalina.properties中记录了tomcat三种类加载器中分别需要加载一些什么类的信息。

  而CatalinaProperties类正是用来解析此文件的,以告诉tomcat,哪种类加载器,加载哪些类。

 

  我们来看看这个类的源代码:

 

Java代码 复制代码 收藏代码
  1. /**
  2. * Utility class to read the bootstrap Catalina configuration.
  3. * 读取tomcat 的配置文件 catalina.properties
  4. * @author Remy Maucherat
  5. * @version $Revision: 467222 $ $Date: 2006-10-24 11:17:11 +0800 (星期二, 24 十月 2006) $
  6. */ 
  7.  
  8. public class CatalinaProperties { 
  9.     // ------------------------------------------------------- Static Variables 
  10.     private static org.apache.juli.logging.Log log= 
  11.         org.apache.juli.logging.LogFactory.getLog( CatalinaProperties.class ); 
  12.     private static Properties properties =null
  13.  
  14.     static
  15.         loadProperties(); 
  16.     } 
  17.  
  18.  
  19.     // --------------------------------------------------------- Public Methods 
  20.  
  21.  
  22.     /**
  23.      * Return specified property value.
  24.      */ 
  25.     public static String getProperty(String name) { 
  26.         return properties.getProperty(name); 
  27.     } 
  28.  
  29.  
  30.     /**
  31.      * Return specified property value.
  32.      */ 
  33.     public static String getProperty(String name, String defaultValue) { 
  34.         return properties.getProperty(name, defaultValue); 
  35.     } 
  36.  
  37.  
  38.     // --------------------------------------------------------- Public Methods 
  39.  
  40.  
  41.     /**
  42.      * 加载配置信息
  43.      * Load properties.
  44.      */ 
  45.     private staticvoid loadProperties() { 
  46.         InputStream is = null
  47.         Throwable error = null
  48.         //第一步: 从系统变量中查找 
  49.         try
  50.             //getConfigUrl()方法的内容为:   System.getProperty("catalina.config"); 
  51.             String configUrl = getConfigUrl(); 
  52.             if (configUrl != null) { 
  53.                 is = (new URL(configUrl)).openStream(); 
  54.             } 
  55.         } catch (Throwable t) { 
  56.             // Ignore 
  57.         } 
  58.  
  59.         //第二步:再从tomcat的conf目录下去找 
  60.         if (is == null) { 
  61.             try
  62.                 File home = new File(getCatalinaBase()); 
  63.                 File conf = new File(home,"conf"); 
  64.                 File properties = new File(conf,"catalina.properties"); 
  65.                 is = new FileInputStream(properties); 
  66.             } catch (Throwable t) { 
  67.                 // Ignore 
  68.             } 
  69.         } 
  70.          
  71.         //还没找到: 则从类路径中加载. 
  72.         if (is == null) { 
  73.             try
  74.                 is = CatalinaProperties.class.getResourceAsStream 
  75.                     ("/org/apache/catalina/startup/catalina.properties"); 
  76.             } catch (Throwable t) { 
  77.                 // Ignore 
  78.             } 
  79.         } 
  80.          
  81.         // 到这里的话,如果找到了,就将配置文件中加载过来 
  82.         if (is != null) { 
  83.             try
  84.                 properties = new Properties(); 
  85.                 properties.load(is); 
  86.                 is.close(); 
  87.             } catch (Throwable t) { 
  88.                 error = t; 
  89.             } 
  90.         } 
  91.  
  92.         if ((is == null) || (error !=null)) { 
  93.             // Do something 
  94.             log.warn("Failed to load catalina.properties", error); 
  95.             // That's fine - we have reasonable defaults. 
  96.             properties=new Properties(); 
  97.         } 
  98.          
  99.         //将配置文件的key-value 设置为 系统变量. 
  100.         // Register the properties as system properties 
  101.         Enumeration enumeration = properties.propertyNames(); 
  102.         while (enumeration.hasMoreElements()) { 
  103.             String name = (String) enumeration.nextElement(); 
  104.             String value = properties.getProperty(name); 
  105.             if (value != null) { 
  106.                 System.setProperty(name, value); 
  107.             } 
  108.         } 
  109.  
  110.     } 
  111.  
  112.  
  113.     /**
  114.      * Get the value of the catalina.home environment variable.
  115.      */ 
  116.     private static String getCatalinaHome() { 
  117.         return System.getProperty("catalina.home"
  118.                                   System.getProperty("user.dir")); 
  119.     } 
  120.      
  121.      
  122.     /**
  123.      * Get the value of the catalina.base environment variable.
  124.      */ 
  125.     private static String getCatalinaBase() { 
  126.         return System.getProperty("catalina.base", getCatalinaHome()); 
  127.     } 
  128.  
  129.  
  130.     /**
  131.      * Get the value of the configuration URL.
  132.      */ 
  133.     private static String getConfigUrl() { 
  134.         return System.getProperty("catalina.config"); 
  135.     } 
  136.  
  137.  
/**
 * Utility class to read the bootstrap Catalina configuration.
 * 读取tomcat 的配置文件 catalina.properties
 * @author Remy Maucherat
 * @version $Revision: 467222 $ $Date: 2006-10-24 11:17:11 +0800 (星期二, 24 十月 2006) $
 */

public class CatalinaProperties {
    // ------------------------------------------------------- Static Variables
    private static org.apache.juli.logging.Log log=
        org.apache.juli.logging.LogFactory.getLog( CatalinaProperties.class );
    private static Properties properties = null;

    static {
        loadProperties();
    }


    // --------------------------------------------------------- Public Methods


    /**
     * Return specified property value.
     */
    public static String getProperty(String name) {
        return properties.getProperty(name);
    }


    /**
     * Return specified property value.
     */
    public static String getProperty(String name, String defaultValue) {
        return properties.getProperty(name, defaultValue);
    }


    // --------------------------------------------------------- Public Methods


    /**
     * 加载配置信息
     * Load properties.
     */
    private static void loadProperties() {
        InputStream is = null;
        Throwable error = null;
        //第一步: 从系统变量中查找
        try {
        	//getConfigUrl()方法的内容为:   System.getProperty("catalina.config");
            String configUrl = getConfigUrl();
            if (configUrl != null) {
                is = (new URL(configUrl)).openStream();
            }
        } catch (Throwable t) {
            // Ignore
        }

        //第二步:再从tomcat的conf目录下去找
        if (is == null) {
            try {
                File home = new File(getCatalinaBase());
                File conf = new File(home, "conf");
                File properties = new File(conf, "catalina.properties");
                is = new FileInputStream(properties);
            } catch (Throwable t) {
                // Ignore
            }
        }
        
        //还没找到: 则从类路径中加载.
        if (is == null) {
            try {
                is = CatalinaProperties.class.getResourceAsStream
                    ("/org/apache/catalina/startup/catalina.properties");
            } catch (Throwable t) {
                // Ignore
            }
        }
        
        // 到这里的话,如果找到了,就将配置文件中加载过来
        if (is != null) {
            try {
                properties = new Properties();
                properties.load(is);
                is.close();
            } catch (Throwable t) {
                error = t;
            }
        }

        if ((is == null) || (error != null)) {
            // Do something
            log.warn("Failed to load catalina.properties", error);
            // That's fine - we have reasonable defaults.
            properties=new Properties();
        }
        
        //将配置文件的key-value 设置为 系统变量.
        // Register the properties as system properties
        Enumeration enumeration = properties.propertyNames();
        while (enumeration.hasMoreElements()) {
            String name = (String) enumeration.nextElement();
            String value = properties.getProperty(name);
            if (value != null) {
                System.setProperty(name, value);
            }
        }

    }


    /**
     * Get the value of the catalina.home environment variable.
     */
    private static String getCatalinaHome() {
        return System.getProperty("catalina.home",
                                  System.getProperty("user.dir"));
    }
    
    
    /**
     * Get the value of the catalina.base environment variable.
     */
    private static String getCatalinaBase() {
        return System.getProperty("catalina.base", getCatalinaHome());
    }


    /**
     * Get the value of the configuration URL.
     */
    private static String getConfigUrl() {
        return System.getProperty("catalina.config");
    }


}

 

OK, 到此,tomcat初始化类加载器的过程,我们都已经了解了。

可能看到这里,有的人觉得还是不太理解。 好,让我们来总结一下,这个顺序。

   我们按照我们平时常用的操作来看;

  1.我要启动tomcat .. (调用Bootstrap的main  方法)

      (1)tomcat启动之前,需要加载类,需要类加载器。 于是,它去做初始化工作. -----> init()方法.

      (2)init()方法开始工作它再去调用------>initClassLoaders()方法.

      (3)发现需要初始化3个类型的类加载器,再调用---> createClassLoader(name,parent) ,告诉它,我要初始化哪种类型的,它的老爸是谁。

      (4)通过CatalinaProperties 类去联络catalina.properties,获得,这个哪种类加载器加载哪些类的信息。

      (5) 完成初始化,并返回.

  2.tomcat 加载其他资源(待续),启动成功……

  

OK, 文章写完了, 这些内容都是本人自己学习的记录,难免有错误之处,还望大家多提意见,希望能跟各位javaeyer共同交流,达到共同提高的目录。

 

===========================================

类加载器与Tomcat

Tomcat基于标准的类加载,再加上自己的类加载器就形成了类加载器层次。下图详细描述了该层次结构。


从图中可以看到,它与直接委托模型有些不一样,Common类加载器代理Server类加载器,这意味着Web应用类加载器不能访问内部Server类,而Common类加载器可以

Tomcat与System类加载器

        Tomcat使用默认的System类加载器,但是Tomcat通过下面来设置CLASSPATH环境变量: CATALINA_HOME/bin/bootstrap.jar CATALINA_HOME/bin/tomcat-juli.jar JAVA_HOME/lib/tools.jar 由于Tomcat在上面的文件列表中设置了CLASSPATH环境变量,因此在Tomcat生命周期内,system CLASSPATH变量会忽略。只要用户记住Tomcat拥有自己的classpath, 独立于System的Classpath,那么用户将增加的类放置在“Tomcat Common类加载器”一节中列出的目录中,就不会有任何问题。 Bootstrap.jar文件包含了Tomcat启动时所需的类。Tools.jar包含了javac编译器,用于在运行时编译JSP页面为class文件。Tomcat-juli.jar用于Tomcat的Bootstrap日志中。

Tomcat Common类加载器

  下面讲述的是Tomcat的Common类加载器层次结构。Common类加载器加载这些Tomcat和所有Web应用所需要的类时,它从下面路径中加载表7-1中的类文件。  CATALINA_HOME/lib/

 JAR文件                描述

annotation-api.jar 包含注释类 catalina.jar Tomcat6服务器中核心实现类

catalina-ant.jar Tomcat Catalina Ant任务

 catalina-ha.jar High availability包

Catalina-tribes.jar Tomcat通信包

el-api.jar 表达式语言(EL)API

jasper.jar Jasper2编译和运行,将JSP文件转换成servlets

 jasper-el.jar Jasper2表达式语言实现

jasper-jdt.jar Eclipse JDT3.2 Java编译器

 jsp-api.jar JSP 2.1 API

servlet-api.jar Servlet 2.5 API

 tomcat-coyote.jar Tomcat连接器和有用类的集合

 tomcat-dbcp.jar 提供数据库链接池机制和其他对象链接池服务

 tomcat-i18n-**.jar 可用的国际化JAR包 

尽管开发人员可以引用其所有的API,但是不应允许他们在CATALINA_HOME/lib下放置自己的类和JAR文件。如果开发人员希望所有类和JAR文件对于所有Web应用程序都是可见的,那么应该把它们放置在Shared类加载器能够访问到的位置。注意这个不适用于第三方库,例如数据库驱动。因为Tomcat需要经常访问到这些类。 把自定义类放置在Common类加载器路径下,这是不提倡的,主要因为下面两个原因: 自定义类将会造成与Tomcat的兼容性问题。例如,如果用户将XML解析器放置在这个目录下,它会带来无法修复的程序问题;而将Servlet API的老版本入围在该目录下,也会带来同样的问题。 很容易忘记哪个类JAR文件属于开发人员,哪些属于tomcat自有的。因此维护是很棘手的。

Tomcat web应用类加载器

每个Web应用都有自己的类加载器,它们分别在 CATALINA_HOME/webapps/[webapp]/WEB-INF/lib和classes目录下搜寻JAR文件和类文件,Web应用类加载器有些独特,主要在于以下两点:

 第一, 每个Web应用都有自己的类加载器的实例,这意味着Web应用不能看到其他人的类文件。

 第二, Web应用类加载器不采用委托模式,取而代之的是,在请求转发到其他类加载器之前,它试图自己首先加载类。这些行为使得Web应用很容易地覆盖Shared和Common.

类加载器下的类。 应该注意的是,Web应用类加载器并不能覆盖掉Java基本类,这也是不可能的。下列是Web应用类加载器唯一例外的时候,它们通常采用委托模式: Javax.* Org.xml.sax.* Org.w3c.dom.* Org.apache.xerces.* Org.apache.xalan.*  如果父类不能加载它们,则Web应用类加载器将自己试图加载它们。

类加载器顺序

为了回顾一下不同的Tomcat类加载器是如何一直协同工作的,下面假设一个单独的应用请求一个类,类加载器将按照下列顺序查找加载:

 1) Bootstrap类加载器在Java核心类中搜寻;

 2) 这里是不是应该还有一个Extended类加载器在Java的扩展包中搜寻加载相关的类??

 3) System类加载器在下列位置中搜寻: a) CATALINA_HOME/bin/bootstrap.jar b) CATALINA_HOME/bin/tomcat-juli.jar c) JAVA_HOME/lib/tools.jar

4) Web应用类加载器在CATALINA_HOME/webapps/[webapp]/WEB-INF/lib和classes目录下搜寻

5) Common类加载器将在CATALINA_HOME/lib目录下搜寻,加载该目录下的所有类和JAR文件。

动态类重载

前面讨论过,只要类加载器加载了一个类,它就将缓存该类。这意味着后面对该类的请求将会获得缓存的拷贝。因此,如果在JVM运行期间,该类在文件系统改变了,则新的类会被忽视。 然而,因为Tomcat使用自己的类加载器加载每个Web应用,所以它只能通过停止Web应用程序,然后使用新的类加载器重载该类,来实现动态类的重载。Web应用初始的类加载器就被孤立了,就会被JVM在空闲时当作垃圾收集。这种消除了当前的类版本部署时,需要重启JVM。 我们可以通过下面两种机制来实现Tomcat重载Web应用:  配置Tomcat搜寻WEB-INF/classes和WEB-INF/lib目录下的改变。  使用Tomcat管理应用程序来显式地重载Web应用。 Tomcat不会告诉它的类加载器转储它们的缓存,然后从磁盘上重载。而是当它检测到改变或者收到显式重载指令时,它会重载和重启所有Web应用。 

 

========================================================================

 

看完了Java类装载器,我们再来看看应用服务器(Tomcat)对类加载器的使用,每个应用服务器都有一套自己的类加载器体系,从而与Java的类加载器区别开以达到自己与应用程序隔离的目的.

Tomcat的类加载器体系为:Bootstrap--->System--->Common---->Catalina,Shared

Shared------>Webapp1,Webapp2 .....

下面是对每个类加载器的定义:

1.Bootstrap加载器在这里是Java里的Bootstrap和ExtClassLoader的总称,负责加载Java核心包的类,和<Java_Home>/jre/lib/ext目录下的类.通常我们开发人员并不关心.我想只要是java程序这些肯定是必要的

2.System就是系统加载器,一般是AppClassLoader,负责加载ClassPath环境变量设置目录下的值,这个我们开发人员会非常关注,但是在Tomcat里面,虽然用AppClassLoader类加载器,但我们设置的ClassPath对它没有影响(如果有影响,那就麻烦了,将会导致Tomcat运行不稳定),为什么呢,因为tomcat每次启动的时候都会在命令行窗口中都会重新设置Classpath值为:<catalina_Home>/bin/bootstrap.jar和<java_Home>/lib/tools.jar,所以这里面的类一般对应用程序不可见的.除非你设置了

3.Common类加载器负责加载TomcatHOME/common/class下的.Class文件和common/lib中的jar包,这些类可以被Tomcat内核和每个Web应用程序都可以看见,一般放公用的一些重要的类,如servlet.jar等

4.Catalina类加载器从server/classes和server/lib下加载类,Catalina加载的类只对Tomcat服务器内核可见,对Web应用程序不可见,对于运行Tomcat内核的线程,它的上下文类加载器就是Catalina类加载器

5.Shared类加载器负责从share/classes和share/lib中加载类,它加载的类只对所有Web应用程序有效,对Tomcat不可见.

6.WebappX类加载器负责加载Web应用程序的/web-INF/classes和lib目录下的类,只对当前Web应用程序有效,对其他Web应用程序无效,对于运行每个Web应用程序的线程,他们的上下文类加载器就是它们各自的WebappX类加载器

总结:从以上6点可以看出Tomcat提供了完整的类加载器体系,并且分工非常的明确,因而非常访问我们进行Web应用程序的设置和部署.

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值