Tomcat ClassLoader研究

Tomcat的ClassLoader层次结构:

 

      Bootstrap
|
System
|
Common
/ /
Catalina Shared
/ /
Webapp1 Webapp2 ...

 

 

源代码如下:

org.apache.catalina.startup.Bootstrap类(tomcat主类)

 

[java] view plaincopy
 
  1. public final class Bootstrap {  
  2.     // ------------------------------------------------------- Static Variables  
  3.     /** 
  4.      * Debugging detail level for processing the startup. 
  5.      */  
  6.     private static int debug = 0;  
  7.     // ----------------------------------------------------------- Main Program  
  8.     /** 
  9.      * The main program for the bootstrap. 
  10.      * 
  11.      * @param args Command line arguments to be processed 
  12.      */  
  13.     public static void main(String args[]) {  
  14.         // Set the debug flag appropriately  
  15.         for (int i = 0; i < args.length; i++)  {  
  16.             if ("-debug".equals(args[i]))  
  17.                 debug = 1;  
  18.         }  
  19.         // Configure catalina.base from catalina.home if not yet set  
  20.         if (System.getProperty("catalina.base") == null)  
  21.             System.setProperty("catalina.base", getCatalinaHome());  
  22.         // Construct the class loaders we will need  
  23.         ClassLoader commonLoader = null;  
  24.         ClassLoader catalinaLoader = null;  
  25.         ClassLoader sharedLoader = null;  
  26.         try {  
  27.             File unpacked[] = new File[1];  
  28.             File packed[] = new File[1];  
  29.             File packed2[] = new File[2];  
  30.             ClassLoaderFactory.setDebug(debug);  
  31.             unpacked[0] = new File(getCatalinaHome(),  
  32.                                    "common" + File.separator + "classes");  
  33.             packed2[0] = new File(getCatalinaHome(),  
  34.                                   "common" + File.separator + "endorsed");  
  35.             packed2[1] = new File(getCatalinaHome(),  
  36.                                   "common" + File.separator + "lib");  
  37.             commonLoader =  
  38.                 ClassLoaderFactory.createClassLoader(unpacked, packed2, null);  
  39.             unpacked[0] = new File(getCatalinaHome(),  
  40.                                    "server" + File.separator + "classes");  
  41.             packed[0] = new File(getCatalinaHome(),  
  42.                                  "server" + File.separator + "lib");  
  43.             catalinaLoader =  
  44.                 ClassLoaderFactory.createClassLoader(unpacked, packed,  
  45.                                                      commonLoader);  
  46.             unpacked[0] = new File(getCatalinaBase(),  
  47.                                    "shared" + File.separator + "classes");  
  48.             packed[0] = new File(getCatalinaBase(),  
  49.                                  "shared" + File.separator + "lib");  
  50.             sharedLoader =  
  51.                 ClassLoaderFactory.createClassLoader(unpacked, packed,  
  52.                                                      commonLoader);  
  53.         } catch (Throwable t) {  
  54.             log("Class loader creation threw exception", t);  
  55.             System.exit(1);  
  56.         }  
  57.         Thread.currentThread().setContextClassLoader(catalinaLoader);  
  58.         // Load our startup class and call its process() method  
  59.         try {  
  60.             SecurityClassLoad.securityClassLoad(catalinaLoader);  
  61.             // Instantiate a startup class instance  
  62.             if (debug >= 1)  
  63.                 log("Loading startup class");  
  64.             Class startupClass =  
  65.                 catalinaLoader.loadClass  
  66.                 ("org.apache.catalina.startup.Catalina");  
  67.             Object startupInstance = startupClass.newInstance();  
  68.             // Set the shared extensions class loader  
  69.             if (debug >= 1)  
  70.                 log("Setting startup class properties");  
  71.             String methodName = "setParentClassLoader";  
  72.             Class paramTypes[] = new Class[1];  
  73.             paramTypes[0] = Class.forName("java.lang.ClassLoader");  
  74.             Object paramValues[] = new Object[1];  
  75.             paramValues[0] = sharedLoader;  
  76.             Method method =  
  77.                 startupInstance.getClass().getMethod(methodName, paramTypes);  
  78.             method.invoke(startupInstance, paramValues);  
  79.             // Call the process() method  
  80.             if (debug >= 1)  
  81.                 log("Calling startup class process() method");  
  82.             methodName = "process";  
  83.             paramTypes = new Class[1];  
  84.             paramTypes[0] = args.getClass();  
  85.             paramValues = new Object[1];  
  86.             paramValues[0] = args;  
  87.             method =  
  88.                 startupInstance.getClass().getMethod(methodName, paramTypes);  
  89.             method.invoke(startupInstance, paramValues);  
  90.         } catch (Exception e) {  
  91.             System.out.println("Exception during startup processing");  
  92.             e.printStackTrace(System.out);  
  93.             System.exit(2);  
  94.         }  
  95.     }  

 

其中:

commonLoader =
                ClassLoaderFactory.createClassLoader(unpacked, packed2, null);

创建common classloader,以AppClassLoader为父ClassLoader

catalinaLoader =
                ClassLoaderFactory.createClassLoader(unpacked, packed,
                                                     commonLoader);

创建catalina classloader,以common classloader为父classloader

sharedLoader =
                ClassLoaderFactory.createClassLoader(unpacked, packed,
                                                     commonLoader);

创建share classloader,以common classloader为父classloader

 

Thread.currentThread().setContextClassLoader(catalinaLoader);

设置ContextClassLoader为catalina classloader

 

org.apache.catalina.startup.ClassLoaderFactory类

 

[java] view plaincopy
 
  1. public static ClassLoader createClassLoader(File unpacked[],  
  2.                                                 File packed[],  
  3.                                                 ClassLoader parent)  
  4.         throws Exception {  
  5.         if (debug >= 1)  
  6.             log("Creating new class loader");  
  7.         // Construct the "class path" for this class loader  
  8.         ArrayList list = new ArrayList();  
  9.         // Add unpacked directories  
  10.         if (unpacked != null) {  
  11.             for (int i = 0; i < unpacked.length; i++)  {  
  12.                 File file = unpacked[i];  
  13.                 if (!file.isDirectory() || !file.exists() || !file.canRead())  
  14.                     continue;  
  15.                 if (debug >= 1)  
  16.                     log("  Including directory " + file.getAbsolutePath());  
  17.                 URL url = new URL("file", null,  
  18.                                   file.getCanonicalPath() + File.separator);  
  19.                 list.add(url.toString());  
  20.             }  
  21.         }  
  22.         // Add packed directory JAR files  
  23.         if (packed != null) {  
  24.             for (int i = 0; i < packed.length; i++) {  
  25.                 File directory = packed[i];  
  26.                 if (!directory.isDirectory() || !directory.exists() ||  
  27.                     !directory.canRead())  
  28.                     continue;  
  29.                 String filenames[] = directory.list();  
  30.                 for (int j = 0; j < filenames.length; j++) {  
  31.                     String filename = filenames[j].toLowerCase();  
  32.                     if (!filename.endsWith(".jar"))  
  33.                         continue;  
  34.                     File file = new File(directory, filenames[j]);  
  35.                     if (debug >= 1)  
  36.                         log("  Including jar file " + file.getAbsolutePath());  
  37.                     URL url = new URL("file", null,  
  38.                                       file.getCanonicalPath());  
  39.                     list.add(url.toString());  
  40.                 }  
  41.             }  
  42.         }  
  43.         // Construct the class loader itself  
  44.         String array[] = (String[]) list.toArray(new String[list.size()]);  
  45.         StandardClassLoader classLoader = null;  
  46.         if (parent == null)  
  47.             classLoader = new StandardClassLoader(array);  
  48.         else  
  49.             classLoader = new StandardClassLoader(array, parent);  
  50.         classLoader.setDelegate(true);  
  51.         return (classLoader);  
  52.     }  

 

ClassLoaderFactory创建的是StandardClassLoader(org.apache.catalina.loader包中)

 

由Bootstrap类调用Catalina类的process()方法,再调用execute()方法,再调用start()来启动Server实例。

 

当Tomcat开启每个Context时,是调用的StandardContext的start()方法,其中:

设置Loader为WebappLoader

 

[java] view plaincopy
 
  1. if (getLoader() == null) {      // (2) Required by Manager  
  2.             if (getPrivileged()) {  
  3.                 if (debug >= 1)  
  4.                     log("Configuring privileged default Loader");  
  5.                 setLoader(new WebappLoader(this.getClass().getClassLoader()));  
  6.             } else {  
  7.                 if (debug >= 1)  
  8.                     log("Configuring non-privileged default Loader");  
  9.                 setLoader(new WebappLoader(getParentClassLoader()));  
  10.             }  
  11.         }  

 

然后调用:bindThread(),设置当前线程的ClassLoader为WebappLoader的ClassLoader,即为WebappClassLoader

 

[java] view plaincopy
 
  1. private ClassLoader bindThread() {  
  2.         ClassLoader oldContextClassLoader =   
  3.             Thread.currentThread().getContextClassLoader();  
  4.         if (getResources() == null)  
  5.             return oldContextClassLoader;  
  6.         Thread.currentThread().setContextClassLoader  
  7.             (getLoader().getClassLoader());  
  8.         DirContextURLStreamHandler.bind(getResources());  
  9.         if (isUseNaming()) {  
  10.             try {  
  11.                 ContextBindings.bindThread(this, this);  
  12.             } catch (NamingException e) {  
  13.                 // Silent catch, as this is a normal case during the early  
  14.                 // startup stages  
  15.             }  
  16.         }  
  17.         return oldContextClassLoader;  
  18.     }  

 

WebappLoader的ClassLoader的赋值如下:

 

[java] view plaincopy
 
  1. private String loaderClass =  
  2.         "org.apache.catalina.loader.WebappClassLoader";  
  3. ......  
  4. public void start() throws LifecycleException {  
  5.         // Validate and update our current component state  
  6.         if (started)  
  7.             throw new LifecycleException  
  8.                 (sm.getString("webappLoader.alreadyStarted"));  
  9.         if (debug >= 1)  
  10.             log(sm.getString("webappLoader.starting"));  
  11.         lifecycle.fireLifecycleEvent(START_EVENT, null);  
  12.         started = true;  
  13.         if (container.getResources() == null)  
  14.             return;  
  15.         // Register a stream handler factory for the JNDI protocol  
  16.         URLStreamHandlerFactory streamHandlerFactory =  
  17.             new DirContextURLStreamHandlerFactory();  
  18.         try {  
  19.             URL.setURLStreamHandlerFactory(streamHandlerFactory);  
  20.         } catch (Throwable t) {  
  21.             // Ignore the error here.  
  22.         }  
  23.         // Construct a class loader based on our current repositories list  
  24.         try {  
  25.             classLoader = createClassLoader();  
  26.             classLoader.setResources(container.getResources());  
  27.             classLoader.setDebug(this.debug);  
  28.             classLoader.setDelegate(this.delegate);  
  29.             if (container instanceof StandardContext)  
  30.                 classLoader.setAntiJARLocking(((StandardContext) container).getAntiJARLocking());  
  31.             for (int i = 0; i < repositories.length; i++) {  
  32.                 classLoader.addRepository(repositories[i]);  
  33.             }  
  34.             // Configure our repositories  
  35.             setRepositories();  
  36.             setClassPath();  
  37.             setPermissions();  
  38.             if (classLoader instanceof Lifecycle)  
  39.                 ((Lifecycle) classLoader).start();  
  40.             // Binding the Webapp class loader to the directory context  
  41.             DirContextURLStreamHandler.bind  
  42.                 ((ClassLoader) classLoader, this.container.getResources());  
  43.         } catch (Throwable t) {  
  44.             throw new LifecycleException("start: ", t);  
  45.         }  
  46.         // Validate that all required packages are actually available  
  47.         validatePackages();  
  48.         // Start our background thread if we are reloadable  
  49.         if (reloadable) {  
  50.             log(sm.getString("webappLoader.reloading"));  
  51.             try {  
  52.                 threadStart();  
  53.             } catch (IllegalStateException e) {  
  54.                 throw new LifecycleException(e);  
  55.             }  
  56.         }  
  57.     }  
  58. ......  
  59. private WebappClassLoader createClassLoader()  
  60.         throws Exception {  
  61.         Class clazz = Class.forName(loaderClass);  
  62.         WebappClassLoader classLoader = null;  
  63.         if (parentClassLoader == null) {  
  64.             // Will cause a ClassCast is the class does not extend WCL, but  
  65.             // this is on purpose (the exception will be caught and rethrown)  
  66.             classLoader = (WebappClassLoader) clazz.newInstance();  
  67.         } else {  
  68.             Class[] argTypes = { ClassLoader.class };  
  69.             Object[] args = { parentClassLoader };  
  70.             Constructor constr = clazz.getConstructor(argTypes);  
  71.             classLoader = (WebappClassLoader) constr.newInstance(args);  
  72.         }  
  73.         return classLoader;  
  74.     }  

 

 

 

在WebappClassLoader中,其findClass的搜索顺序与一般的ClassLoader的搜索顺序不同。

一般的ClassLoader的搜索顺序为:

将其委托给父ClassLoader,如果父ClassLoader不能载入相应类,则才交给自己处理

但是WebappClassLoader中,其是先由自己来处理,如果不行再委托给父ClassLoader

相关源代码如下:

 

[java] view plaincopy
 
  1. Class clazz = null;  
  2.        try {  
  3.            if (debug >= 4)  
  4.                log("      findClassInternal(" + name + ")");  
  5.            try {  
  6.                clazz = findClassInternal(name);  
  7.            } catch(ClassNotFoundException cnfe) {  
  8.                if (!hasExternalRepositories) {  
  9.                    throw cnfe;  
  10.                }  
  11.            } catch(AccessControlException ace) {  
  12.                ace.printStackTrace();  
  13.                throw new ClassNotFoundException(name);  
  14.            } catch (RuntimeException e) {  
  15.                if (debug >= 4)  
  16.                    log("      -->RuntimeException Rethrown", e);  
  17.                throw e;  
  18.            }  
  19.            if ((clazz == null) && hasExternalRepositories) {  
  20.                try {  
  21.                    clazz = super.findClass(name);  
  22.                } catch(AccessControlException ace) {  
  23.                    throw new ClassNotFoundException(name);  
  24.                } catch (RuntimeException e) {  
  25.                    if (debug >= 4)  
  26.                        log("      -->RuntimeException Rethrown", e);  
  27.                    throw e;  
  28.                }  
  29.            }  
  30.            if (clazz == null) {  
  31.                if (debug >= 3)  
  32.                    log("    --> Returning ClassNotFoundException");  
  33.                throw new ClassNotFoundException(name);  
  34.            }  
  35.        } catch (ClassNotFoundException e) {  
  36.            if (debug >= 3)  
  37.                log("    --> Passing on ClassNotFoundException", e);  
  38.            throw e;  
  39.        }  

 

 

 

以下引自tomcat的说明文档,说明了加载类的顺序

Therefore, from the perspective of a web application, class or resource loading looks in the following repositories, in this order:

    • /WEB-INF/classes of your web application
    • /WEB-INF/lib/*.jar of your web application
    • Bootstrap classes of your JVM
    • System class loader classses (described above)
    • $CATALINA_HOME/common/classes
    • $CATALINA_HOME/common/endorsed/*.jar
    • $CATALINA_HOME/common/lib/*.jar
    • $CATALINA_BASE/shared/classes
    • $CATALINA_BASE/shared/lib/*.jar

posted on 2015-03-10 10:33  帅胡  阅读(548)  评论(0编辑  收藏  举报

导航