Tomcat启动部分源代码分析(二) -- 初始化

二. 初始化

1. 首先是Bootstrap的#init()操作。

Java代码 Tomcat启动部分源代码分析(二) -- 初始化 - guoyang1982 - 游龙

  1. public void init() throws Exception {   
  2.   
  3.     // 设定Catalina   
  4.     setCatalinaHome();   
  5.     setCatalinaBase();   
  6.   
  7.     // 初始化ClassLoader   
  8.     initClassLoaders();   
  9.   
  10.     // 设置线程的上下文的ClassLoader   
  11.     Thread.currentThread().setContextClassLoader(catalinaLoader);   
  12.   
  13.     SecurityClassLoad.securityClassLoad(catalinaLoader);   
  14.   
  15.     // 实例化Catalina对象   
  16.     if (log.isDebugEnabled())   
  17.         log.debug("Loading startup class");   
  18.     Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");   
  19.     Object startupInstance = startupClass.newInstance();   
  20.   
  21.     if (log.isDebugEnabled())   
  22.         log.debug("Setting startup class properties");   
  23.     String methodName = "setParentClassLoader";   
  24.     Class paramTypes[] = new Class[1];   
  25.     paramTypes[0] = Class.forName("java.lang.ClassLoader");   
  26.     Object paramValues[] = new Object[1];   
  27.     paramValues[0] = sharedLoader;   
  28.     Method method = startupInstance.getClass().getMethod(methodName, paramTypes);   
  29.     method.invoke(startupInstance, paramValues);   
  30.   
  31.     catalinaDaemon = startupInstance;   
  32.   
  33. }  

public void init() throws Exception { // 设定Catalina setCatalinaHome(); setCatalinaBase(); // 初始化ClassLoader initClassLoaders(); // 设置线程的上下文的ClassLoader Thread.currentThread().setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader); // 实例化Catalina对象 if (log.isDebugEnabled()) log.debug("Loading startup class"); Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); 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; }

init方法主要是对Catalina环境的设定和ClassLoader的初始化,下面来一个一个的查看:

① Catalina的环境设定:

这里主要是setCatalinaHome()和setCatalinaBase()这两个方法

Java代码 Tomcat启动部分源代码分析(二) -- 初始化 - guoyang1982 - 游龙

  1. private void setCatalinaHome() {   
  2.     if (System.getProperty("catalina.home") != null)   
  3.         return;   
  4.     File bootstrapJar = new File(System.getProperty("user.dir"), "bootstrap.jar");   
  5.     if (bootstrapJar.exists()) {   
  6.         try {   
  7.             System.setProperty("catalina.home",   
  8.                     (new File(System.getProperty("user.dir"), "..")).getCanonicalPath());   
  9.         } catch (Exception e) {   
  10.             // Ignore   
  11.             System.setProperty("catalina.home", System.getProperty("user.dir"));   
  12.         }   
  13.     } else {   
  14.         System.setProperty("catalina.home", System.getProperty("user.dir"));   
  15.     }   
  16.   
  17. }  

private void setCatalinaHome() { if (System.getProperty("catalina.home") != null) return; File bootstrapJar = new File(System.getProperty("user.dir"), "bootstrap.jar"); if (bootstrapJar.exists()) { try { System.setProperty("catalina.home", (new File(System.getProperty("user.dir"), "..")).getCanonicalPath()); } catch (Exception e) { // Ignore System.setProperty("catalina.home", System.getProperty("user.dir")); } } else { System.setProperty("catalina.home", System.getProperty("user.dir")); } }

这个方法主要是对catalina.home的设定。

Java代码 Tomcat启动部分源代码分析(二) -- 初始化 - guoyang1982 - 游龙

  1. private void setCatalinaBase() {   
  2.   
  3.     if (System.getProperty("catalina.base") != null)   
  4.         return;   
  5.     if (System.getProperty("catalina.home") != null)   
  6.         System.setProperty("catalina.base", System.getProperty("catalina.home"));   
  7.     else  
  8.         System.setProperty("catalina.base", System.getProperty("user.dir"));   
  9.   
  10. }  

private void setCatalinaBase() { if (System.getProperty("catalina.base") != null) return; if (System.getProperty("catalina.home") != null) System.setProperty("catalina.base", System.getProperty("catalina.home")); else System.setProperty("catalina.base", System.getProperty("user.dir")); }

这个方法主要是对catalina.base的设定。

② 初始化ClassLoader

ClassLoader在Tomcat的启动中是比较重要的部分,我在Apache Tomcat的网站上找到了一些关于Tomcat ClassLoader的说明,简单的介绍下。

这个是Tomcat的ClassLoader的继承结构

       Bootstrap

          |

       System

          |

       Common

       /     \

  Webapp1   Webapp2 ...

Bootstrap是JVM提供的ClassLoader,它主要负责载入$JAVA_HOME/jre/lib/ext下的类文件。

System主要负责载入CLASSPATH下的类文件,这些类对Tomcat容器内部的类和Web应用程序都可见。

Common主要载入$CATALINA_HOME/lib下面的类文件,也是同时对Tomcat容器内部的类和Web应用程序都可见。

WebappX载入每一个Web应用程序的类文件,包括/WEB-INF/classes下的类文件和/WEB-INF/lib下的jar文件。并且WebApp之间是不可见的。

而Tomcat5.5的ClassLoader结构是与Tomcat6.0有所不同的

       Bootstrap

          |

       System

          |

       Common

      /      \

Catalina   Shared

             /   \

        Webapp1  Webapp2 ...

在Common下边还有Catalina和Shared两个ClassLoader(Tomcat6.0也是有的,只是默认没有使用)。Catalina负责载入Tomcat内部可见而WebApp不可见的类文件。Shared负责载入所有WebApp都可见的类文件。

原文的网址是: http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html

http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html

简单介绍过之后,看一下#initClassLoaders()方法,此方法中实现了ClassLoader的初始化:

Java代码 Tomcat启动部分源代码分析(二) -- 初始化 - guoyang1982 - 游龙

  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.         // 父loader是commonloader   
  9.         catalinaLoader = createClassLoader("server", commonLoader);   
  10.         // 父loader是commonloader   
  11.         sharedLoader = createClassLoader("shared", commonLoader);   
  12.     } catch (Throwable t) {   
  13.         log.error("Class loader creation threw exception", t);   
  14.         System.exit(1);   
  15.     }   
  16. }  

private void initClassLoaders() { try { 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(); } // 父loader是commonloader catalinaLoader = createClassLoader("server", commonLoader); // 父loader是commonloader sharedLoader = createClassLoader("shared", commonLoader); } catch (Throwable t) { log.error("Class loader creation threw exception", t); System.exit(1); } }

这个方法很容易看懂,就是对3个ClassLoader的初始化。其中调用了createClassLoader()方法,这个也是比较重要的,下面来看下:

Java代码 Tomcat启动部分源代码分析(二) -- 初始化 - guoyang1982 - 游龙

  1. private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception {   
  2.   
  3.     // 获取与ClassLoader相关的属性值,可能为common.loader, server.loader, shared.loader   
  4.     // 定义在org/apache/catalina/startup/catalina.properties   
  5.     String value = CatalinaProperties.getProperty(name + ".loader");   
  6.     // catalinaLoader和sharedLoader默认没有配置值,所以默认使用父loader,就是Common loader   
  7.     if ((value == null) || (value.equals("")))// 如果没有定义,那么使用父loader   
  8.         return parent;   
  9.   
  10.     // 路径位置   
  11.     ArrayList repositoryLocations = new ArrayList();   
  12.     // 路径类型   
  13.     ArrayList repositoryTypes = new ArrayList();   
  14.     int i;   
  15.   
  16.     // ClassLoader将载入的路径是用","分割的   
  17.     StringTokenizer tokenizer = new StringTokenizer(value, ",");   
  18.     while (tokenizer.hasMoreElements()) {   
  19.         // 类文件路径   
  20.         String repository = tokenizer.nextToken();   
  21.   
  22.         // 是否对目录进行过替换   
  23.         boolean replace = false;   
  24.         String before = repository;   
  25.   
  26.         // 对"${catalina.home}"进行替换   
  27.         while ((i = repository.indexOf(CATALINA_HOME_TOKEN)) >= 0) {   
  28.             replace = true;   
  29.             if (i > 0) {// 如果"${catalina.home}"不是在字符串首   
  30.                 repository = repository.substring(0, i) + getCatalinaHome()   
  31.                         + repository.substring(i + CATALINA_HOME_TOKEN.length());   
  32.             } else {// 如果"${catalina.home}"在字符串首   
  33.                 repository = getCatalinaHome()   
  34.                         + repository.substring(CATALINA_HOME_TOKEN.length());   
  35.             }   
  36.         }   
  37.         // 对"${catalina.base}"进行替换   
  38.         while ((i = repository.indexOf(CATALINA_BASE_TOKEN)) >= 0) {   
  39.             replace = true;   
  40.             if (i > 0) {   
  41.                 repository = repository.substring(0, i) + getCatalinaBase()   
  42.                         + repository.substring(i + CATALINA_BASE_TOKEN.length());   
  43.             } else {   
  44.                 repository = getCatalinaBase()   
  45.                         + repository.substring(CATALINA_BASE_TOKEN.length());   
  46.             }   
  47.         }   
  48.         if (replace && log.isDebugEnabled())   
  49.             log.debug("Expanded " + before + " to " + replace);   
  50.   
  51.         // Check for a JAR URL repository   
  52.         try {   
  53.             // url用于测试这个目录是否是合法的URL,如果不是,会报出异常,跳到后边。   
  54.             URL url = new URL(repository);   
  55.             // URL   
  56.             repositoryLocations.add(repository);   
  57.             repositoryTypes.add(ClassLoaderFactory.IS_URL);   
  58.             continue;   
  59.         } catch (MalformedURLException e) {   
  60.             // Ignore   
  61.         }   
  62.   
  63.         if (repository.endsWith("*.jar")) {   
  64.             repository = repository.substring(0, repository.length() - "*.jar".length());   
  65.             // jar文件目录   
  66.             repositoryLocations.add(repository);   
  67.             repositoryTypes.add(ClassLoaderFactory.IS_GLOB);   
  68.         } else if (repository.endsWith(".jar")) {   
  69.             // 单独jar文件   
  70.             repositoryLocations.add(repository);   
  71.             repositoryTypes.add(ClassLoaderFactory.IS_JAR);   
  72.         } else {   
  73.             // 目录   
  74.             repositoryLocations.add(repository);   
  75.             repositoryTypes.add(ClassLoaderFactory.IS_DIR);   
  76.         }   
  77.     }   
  78.   
  79.     String[] locations = (String[]) repositoryLocations.toArray(new String[0]);   
  80.     Integer[] types = (Integer[]) repositoryTypes.toArray(new Integer[0]);   
  81.   
  82.     // 返回ClassLoader   
  83.     ClassLoader classLoader = ClassLoaderFactory.createClassLoader(locations, types, parent);   
  84.   
  85.     // MBean server,我不懂JMX~~   
  86.     MBeanServer mBeanServer = null;   
  87.     if (MBeanServerFactory.findMBeanServer(null).size() > 0) {   
  88.         mBeanServer = (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0);   
  89.     } else {   
  90.         mBeanServer = MBeanServerFactory.createMBeanServer();   
  91.     }   
  92.   
  93.     ObjectName objectName = new ObjectName("Catalina:type=ServerClassLoader,name=" + name);   
  94.     mBeanServer.registerMBean(classLoader, objectName);   
  95.   
  96.     return classLoader;   
  97.   
  98. }  

private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception { // 获取与ClassLoader相关的属性值,可能为common.loader, server.loader, shared.loader // 定义在org/apache/catalina/startup/catalina.properties String value = CatalinaProperties.getProperty(name + ".loader"); // catalinaLoader和sharedLoader默认没有配置值,所以默认使用父loader,就是Common loader if ((value == null) || (value.equals("")))// 如果没有定义,那么使用父loader return parent; // 路径位置 ArrayList repositoryLocations = new ArrayList(); // 路径类型 ArrayList repositoryTypes = new ArrayList(); int i; // ClassLoader将载入的路径是用","分割的 StringTokenizer tokenizer = new StringTokenizer(value, ","); while (tokenizer.hasMoreElements()) { // 类文件路径 String repository = tokenizer.nextToken(); // 是否对目录进行过替换 boolean replace = false; String before = repository; // 对"${catalina.home}"进行替换 while ((i = repository.indexOf(CATALINA_HOME_TOKEN)) >= 0) { replace = true; if (i > 0) {// 如果"${catalina.home}"不是在字符串首 repository = repository.substring(0, i) + getCatalinaHome() + repository.substring(i + CATALINA_HOME_TOKEN.length()); } else {// 如果"${catalina.home}"在字符串首 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 " + replace); // Check for a JAR URL repository try { // url用于测试这个目录是否是合法的URL,如果不是,会报出异常,跳到后边。 URL url = new URL(repository); // URL 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()); // jar文件目录 repositoryLocations.add(repository); repositoryTypes.add(ClassLoaderFactory.IS_GLOB); } else if (repository.endsWith(".jar")) { // 单独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 classLoader = ClassLoaderFactory.createClassLoader(locations, types, parent); // MBean server,我不懂JMX~~ MBeanServer mBeanServer = null; if (MBeanServerFactory.findMBeanServer(null).size() > 0) { mBeanServer = (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0); } else { mBeanServer = MBeanServerFactory.createMBeanServer(); } ObjectName objectName = new ObjectName("Catalina:type=ServerClassLoader,name=" + name); mBeanServer.registerMBean(classLoader, objectName); return classLoader; }

关键点给标上了一些注释,其中返回ClassLoader的那一行值得关注一下,

ClassLoader classLoader = ClassLoaderFactory.createClassLoader(locations, types, parent);

自然,要看一下ClassLoaderFactory#createClassLoader()到底做了什么。

Java代码 Tomcat启动部分源代码分析(二) -- 初始化 - guoyang1982 - 游龙

  1. public static ClassLoader createClassLoader(String locations[], Integer types[],   
  2.             ClassLoader parent) throws Exception {   
  3.   
  4.         if (log.isDebugEnabled())   
  5.             log.debug("Creating new class loader");   
  6.   
  7.         // Construct the "class path" for this class loader   
  8.         ArrayList list = new ArrayList();   
  9.   
  10.         // 确保locations和types不为空且长度相同   
  11.         if (locations != null && types != null && locations.length == types.length) {   
  12.             // 查看每一个location   
  13.             for (int i = 0; i < locations.length; i++) {   
  14.                 String location = locations[i];   
  15.                 if (types[i] == IS_URL) {// URL   
  16.                     URL url = new URL(location);   
  17.                     if (log.isDebugEnabled())   
  18.                         log.debug("  Including URL " + url);   
  19.                     list.add(url);   
  20.                 } else if (types[i] == IS_DIR) {// 目录   
  21.                     File directory = new File(location);   
  22.                     directory = new File(directory.getCanonicalPath());   
  23.                     if (!directory.exists() || !directory.isDirectory() || !directory.canRead())   
  24.                         continue;   
  25.                     URL url = directory.toURI().toURL();   
  26.                     if (log.isDebugEnabled())   
  27.                         log.debug("  Including directory " + url);   
  28.                     list.add(url);   
  29.                 } else if (types[i] == IS_JAR) {// 单独的jar文件   
  30.                     File file = new File(location);   
  31.                     file = new File(file.getCanonicalPath());   
  32.                     if (!file.exists() || !file.canRead())   
  33.                         continue;   
  34.                     URL url = file.toURI().toURL();   
  35.                     if (log.isDebugEnabled())   
  36.                         log.debug("  Including jar file " + url);   
  37.                     list.add(url);   
  38.                 } else if (types[i] == IS_GLOB) {// jar目录   
  39.                     File directory = new File(location);   
  40.                     if (!directory.exists() || !directory.isDirectory() || !directory.canRead())   
  41.                         continue;   
  42.                     if (log.isDebugEnabled())   
  43.                         log.debug("  Including directory glob " + directory.getAbsolutePath());   
  44.                     String filenames[] = directory.list();   
  45.                     for (int j = 0; j < filenames.length; j++) {   
  46.                         String filename = filenames[j].toLowerCase();   
  47.                         if (!filename.endsWith(".jar"))   
  48.                             continue;   
  49.                         File file = new File(directory, filenames[j]);   
  50.                         file = new File(file.getCanonicalPath());   
  51.                         if (!file.exists() || !file.canRead())   
  52.                             continue;   
  53.                         if (log.isDebugEnabled())   
  54.                             log.debug("    Including glob jar file " + file.getAbsolutePath());   
  55.                         URL url = file.toURI().toURL();   
  56.                         list.add(url);   
  57.                     }   
  58.                 }   
  59.             }   
  60.         }   
  61.   
  62.         // 类文件的URL列表   
  63.         URL[] array = (URL[]) list.toArray(new URL[list.size()]);   
  64.         if (log.isDebugEnabled())   
  65.             for (int i = 0; i < array.length; i++) {   
  66.                 log.debug("  location " + i + " is " + array[i]);   
  67.             }   
  68.   
  69.         // 继承自URLClassLoader   
  70.         StandardClassLoader classLoader = null;   
  71.         if (parent == null)   
  72.             classLoader = new StandardClassLoader(array);   
  73.         else  
  74.             classLoader = new StandardClassLoader(array, parent);   
  75.         return (classLoader);   
  76.   
  77.     }  

public static ClassLoader createClassLoader(String locations[], Integer types[], ClassLoader parent) throws Exception { if (log.isDebugEnabled()) log.debug("Creating new class loader"); // Construct the "class path" for this class loader ArrayList list = new ArrayList(); // 确保locations和types不为空且长度相同 if (locations != null && types != null && locations.length == types.length) { // 查看每一个location for (int i = 0; i < locations.length; i++) { String location = locations[i]; if (types[i] == IS_URL) {// URL URL url = new URL(location); if (log.isDebugEnabled()) log.debug(" Including URL " + url); list.add(url); } else if (types[i] == IS_DIR) {// 目录 File directory = new File(location); directory = new File(directory.getCanonicalPath()); if (!directory.exists() || !directory.isDirectory() || !directory.canRead()) continue; URL url = directory.toURI().toURL(); if (log.isDebugEnabled()) log.debug(" Including directory " + url); list.add(url); } else if (types[i] == IS_JAR) {// 单独的jar文件 File file = new File(location); file = new File(file.getCanonicalPath()); if (!file.exists() || !file.canRead()) continue; URL url = file.toURI().toURL(); if (log.isDebugEnabled()) log.debug(" Including jar file " + url); list.add(url); } else if (types[i] == IS_GLOB) {// jar目录 File directory = new File(location); if (!directory.exists() || !directory.isDirectory() || !directory.canRead()) continue; if (log.isDebugEnabled()) log.debug(" Including directory glob " + directory.getAbsolutePath()); String filenames[] = directory.list(); for (int j = 0; j < filenames.length; j++) { String filename = filenames[j].toLowerCase(); if (!filename.endsWith(".jar")) continue; File file = new File(directory, filenames[j]); file = new File(file.getCanonicalPath()); if (!file.exists() || !file.canRead()) continue; if (log.isDebugEnabled()) log.debug(" Including glob jar file " + file.getAbsolutePath()); URL url = file.toURI().toURL(); list.add(url); } } } } // 类文件的URL列表 URL[] array = (URL[]) list.toArray(new URL[list.size()]); if (log.isDebugEnabled()) for (int i = 0; i < array.length; i++) { log.debug(" location " + i + " is " + array[i]); } // 继承自URLClassLoader StandardClassLoader classLoader = null; if (parent == null) classLoader = new StandardClassLoader(array); else classLoader = new StandardClassLoader(array, parent); return (classLoader); }

至此,ClassLoader生成完毕,并将需要载入的类文件路径传给了构造函数。

初始化ClassLoader之后,调用了Thread.currentThread().setContextClassLoader(catalinaLoader);这一句包含的内容也很有趣,下面来研究一下:

虽然有些本末倒置之嫌,但还是要介绍一下ClassLoader的相关内容:

与ClassLoader相关的概念主要有4个,分别是基本的Classloader,自定义Classloader,Caller Classloader,当前线程的上下文Classloader,下面一一介绍:

基本的Classloader

⑴最基本的Classloader是Bootstrap Classloader和System Classloader(或者说AppClassLoader)

很多内容借鉴自 http://fyjava.javaeye.com/blog/198119

Bootstrap Classloader

这个Classloader装载Java虚拟机提供的基本运行时刻类($JAVA_HOME/jre/lib),还包括放置在系统扩展目录($ JAVA_HOME/jre/lib/ext)内的JAR文件中的类。这个Classloader是java程序最顶层的Classloader,只有它没有父Classloader。如果你将一个自己写的类或第三方jar包放进$JAVA_HOME/jre/lib/ext目录中,那么它将被 Bootstrap Classloader装载。

System Classloader

System Classloader通常负责装载系统环境变量CLASSPATH中设置的类。

⑵自定义Classloader

在编写应用代码的时候,常常有需要动态加载类和资源,比如显式的调用classLoader.loadClass(“ClassName”),虽然直接使用 ClassLoader.getSystemClassLoader(),可以得到SystemlassLoader来完成这项任务。但是,由于 System Classloader是JVM创建的Classloader,它的职责有限,只适合于普通的java应用程序,在很多复杂场景中不能满足需求,比如在应用服务器中。这时候就需要自行实现一个Classloader的子类。

⑶Caller Classloader

Caller Classloader指的是当前所在的类装载时使用的Classloader,它可能是System Classloader,也可能是一个自定义的Classloader,这里,我们都称之为Caller Classloader。我们可以通过getClass().getClassLoader()来得到Caller Classloader。例如,存在A类,是被AClassLoader所加载,A.class.getClassLoader()为AClassLoader的实例,它就是A.class的Caller Classloader。

如果在A类中使用new关键字,或者Class.forName(String className)和Class.getResource(String resourceName)方法,那么这时也是使用Caller Classloader来装载类和资源。比如在A类中初始化B类:

/**

  * A.java

*/

...

public void foo() {

    B b = new B();

    b.setName("b");

}

那么,B类由当前Classloader,也就是AClassloader装载。同样的,修改上述的foo方法,其实现改为:

Class clazz = Class.forName("foo.B");最终获取到的clazz,也是由AClassLoader所装载。

那么,如何使用指定的Classloader去完成类和资源的装载呢?或者说,当需要去实例化一个Caller Classloader和它的父Classloader都不能装载的类时,怎么办呢?

一个很典型的例子是JAXP,当使用xerces的SAX实现时,我们首先需要通过rt.jar中的 javax.xml.parsers.SAXParserFactory.getInstance()得到xercesImpl.jar中的 org.apache.xerces.jaxp.SAXParserFactoryImpl的实例。由于JAXP的框架接口的class位于 JAVA_HOME/lib/rt.jar中,由Bootstrap Classloader装载,处于Classloader层次结构中的最顶层,而xercesImpl.jar由低层的Classloader装载,也就是说SAXParserFactoryImpl是在SAXParserFactory中实例化的,如前所述,使用SAXParserFactory的Caller Classloader(这里是Bootstrap Classloader)是完成不了这个任务的。

这时,我们就需要了解一下线程上下文Classloader了。

⑷线程上下文Classloader

每个线程都有一个关联的上下文Classloader。如果使用new Thread()方式生成新的线程,新线程将继承其父线程的上下文Classloader。如果程序对线程上下文Classloader没有任何改动的话,程序中所有的线程将都使用System Classloader作为上下文Classloader。

当使用Thread.currentThread().setContextClassLoader(classloader)时,线程上下文 Classloader就变成了指定的Classloader了。此时,在本线程的任意一处地方,调用Thread.currentThread(). getContextClassLoader(),都可以得到前面设置的Classloader。

回到JAXP的例子,假设 xercesImpl.jar只有AClassLoader能装载,现在A.class内部要使用JAXP,但是A.class却不是由 AClassLoader或者它的子Classloader装载的,那么在A.class中,应该这样写才能正确得到xercesImpl的实现:

AClassLoader aClassLoader = new AClassLoader(parent);

Thread.currentThread().setContextClassLoader(aClassLoader);

SAXParserFactory factory = SAXParserFactory.getInstance();

...

JAXP这时就可以通过线程上下文Classloader装载xercesImpl的实现类了,当然,还有一个前提是在配制文件或启动参数中指定了使用xerces作为JAXP的实现。下面是JAXP中的代码片断:

ClassLoader cl = Thread.currentThread().getContextClassLoader();

Class providerClass = cl.loadClass(className);

③ 然后,回到Bootstrap#init()方法中来,载入了ClassLoader后,执行了下述代码

Java代码 Tomcat启动部分源代码分析(二) -- 初始化 - guoyang1982 - 游龙

  1. if (log.isDebugEnabled())   
  2.     log.debug("Loading startup class");   
  3. Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");   
  4. Object startupInstance = startupClass.newInstance();   
  5.   
  6. // 调用org.apache.catalina.startup.Catalina#setParentClassLoader(),并将前面讲到的Shared ClassLoader作为参数传递给它。   
  7. if (log.isDebugEnabled())   
  8.     log.debug("Setting startup class properties");   
  9. String methodName = "setParentClassLoader";   
  10. Class paramTypes[] = new Class[1];   
  11. paramTypes[0] = Class.forName("java.lang.ClassLoader");   
  12. Object paramValues[] = new Object[1];   
  13. paramValues[0] = sharedLoader;   
  14. Method method = startupInstance.getClass().getMethod(methodName, paramTypes);   
  15. method.invoke(startupInstance, paramValues);   
  16.   
  17. // 让catalinaDaemon引用Catalina对象。   
  18. catalinaDaemon = startupInstance;  

if (log.isDebugEnabled()) log.debug("Loading startup class"); Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); // 调用org.apache.catalina.startup.Catalina#setParentClassLoader(),并将前面讲到的Shared ClassLoader作为参数传递给它。 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引用Catalina对象。 catalinaDaemon = startupInstance;

setParentClassLoader()这个方法的定义倒是很简单,如下所示:

Java代码 Tomcat启动部分源代码分析(二) -- 初始化 - guoyang1982 - 游龙

  1. public void setParentClassLoader(ClassLoader parentClassLoader) {   
  2.   
  3.     this.parentClassLoader = parentClassLoader;   
  4.   
  5. }  

public void setParentClassLoader(ClassLoader parentClassLoader) { this.parentClassLoader = parentClassLoader; }

具体Catalina的parentClassLoader有什么作用看到这里还无法知道。

至此,init()方法就简单看完了,下面来看一下Bootstrap#load(String[] arguments)方法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值