`
outlaw
  • 浏览: 29810 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Tomcat ClassLoader研究

阅读更多

 Tomcat ClassLoader研究 收藏

<script type="text/javascript"></script><script type="text/javascript"></script>

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

Tomcat的ClassLoader层次结构:

      Bootstrap


          |


       System


          |


       Common


      /      \


 Catalina   Shared


             /   \


        Webapp1  Webapp2 ...

源代码如下:

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

 

  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类

 

  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

 

  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

 

  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(thisthis);   
  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的赋值如下:

 

  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

相关源代码如下:

 

  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
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics