设为首页 收藏本站
查看: 283|回复: 0

[经验分享] Tomcat ClassLoader研究

[复制链接]

尚未签到

发表于 2017-1-19 06:59:45 | 显示全部楼层 |阅读模式
  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层次结构:

http://tomcat.apache.org/tomcat-4.1-doc/images/void.gifhttp://tomcat.apache.org/tomcat-4.1-doc/images/void.gifhttp://tomcat.apache.org/tomcat-4.1-doc/images/void.gif
http://tomcat.apache.org/tomcat-4.1-doc/images/void.gif
      Bootstrap

|

System

|

Common

/      \

Catalina   Shared

/   \

Webapp1  Webapp2 ...
http://tomcat.apache.org/tomcat-4.1-doc/images/void.gif
http://tomcat.apache.org/tomcat-4.1-doc/images/void.gifhttp://tomcat.apache.org/tomcat-4.1-doc/images/void.gifhttp://tomcat.apache.org/tomcat-4.1-doc/images/void.gif


  源代码如下:
  org.apache.catalina.startup.Bootstrap类(tomcat主类)

view plaincopy to clipboardprint?





  • public final class Bootstrap {   

  •     // ------------------------------------------------------- Static Variables   

  •     /**  
  •      * Debugging detail level for processing the startup.  
  •      */  

  •     private static int debug = 0;   

  •     // ----------------------------------------------------------- Main Program   

  •     /**  
  •      * The main program for the bootstrap.  
  •      *  
  •      * @param args Command line arguments to be processed  
  •      */  

  •     public static void main(String args[]) {   

  •         // Set the debug flag appropriately   

  •         for (int i = 0; i < args.length; i++)  {   

  •             if ("-debug".equals(args))   

  •                 debug = 1;   
  •         }   

  •         // Configure catalina.base from catalina.home if not yet set   

  •         if (System.getProperty("catalina.base") == null)   

  •             System.setProperty("catalina.base", getCatalinaHome());   

  •         // Construct the class loaders we will need   

  •         ClassLoader commonLoader = null;   

  •         ClassLoader catalinaLoader = null;   

  •         ClassLoader sharedLoader = null;   

  •         try {   

  •             File unpacked[] = new File[1];   

  •             File packed[] = new File[1];   

  •             File packed2[] = new File[2];   
  •             ClassLoaderFactory.setDebug(debug);   

  •             unpacked[0] = new File(getCatalinaHome(),   

  •                                    "common" + File.separator + "classes");   

  •             packed2[0] = new File(getCatalinaHome(),   

  •                                   "common" + File.separator + "endorsed");   

  •             packed2[1] = new File(getCatalinaHome(),   

  •                                   "common" + File.separator + "lib");   
  •             commonLoader =   

  •                 ClassLoaderFactory.createClassLoader(unpacked, packed2, null);   

  •             unpacked[0] = new File(getCatalinaHome(),   

  •                                    "server" + File.separator + "classes");   

  •             packed[0] = new File(getCatalinaHome(),   

  •                                  "server" + File.separator + "lib");   
  •             catalinaLoader =   
  •                 ClassLoaderFactory.createClassLoader(unpacked, packed,   
  •                                                      commonLoader);   

  •             unpacked[0] = new File(getCatalinaBase(),   

  •                                    "shared" + File.separator + "classes");   

  •             packed[0] = new File(getCatalinaBase(),   

  •                                  "shared" + File.separator + "lib");   
  •             sharedLoader =   
  •                 ClassLoaderFactory.createClassLoader(unpacked, packed,   
  •                                                      commonLoader);   

  •         } catch (Throwable t) {   

  •             log("Class loader creation threw exception", t);   

  •             System.exit(1);   
  •         }   
  •         Thread.currentThread().setContextClassLoader(catalinaLoader);   

  •         // Load our startup class and call its process() method   

  •         try {   
  •             SecurityClassLoad.securityClassLoad(catalinaLoader);   

  •             // Instantiate a startup class instance   

  •             if (debug >= 1)   

  •                 log("Loading startup class");   
  •             Class startupClass =   
  •                 catalinaLoader.loadClass   

  •                 ("org.apache.catalina.startup.Catalina");   
  •             Object startupInstance = startupClass.newInstance();   

  •             // Set the shared extensions class loader   

  •             if (debug >= 1)   

  •                 log("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);   

  •             // Call the process() method   

  •             if (debug >= 1)   

  •                 log("Calling startup class process() method");   

  •             methodName = "process";   

  •             paramTypes = new Class[1];   

  •             paramTypes[0] = args.getClass();   

  •             paramValues = new Object[1];   

  •             paramValues[0] = args;   
  •             method =   
  •                 startupInstance.getClass().getMethod(methodName, paramTypes);   
  •             method.invoke(startupInstance, paramValues);   

  •         } catch (Exception e) {   

  •             System.out.println("Exception during startup processing");   
  •             e.printStackTrace(System.out);   

  •             System.exit(2);   
  •         }   
  •     }  


  其中:
  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类

view plaincopy to clipboardprint?





  • public static ClassLoader createClassLoader(File unpacked[],   
  •                                                 File packed[],   
  •                                                 ClassLoader parent)   

  •         throws Exception {   

  •         if (debug >= 1)   

  •             log("Creating new class loader");   

  •         // Construct the "class path" for this class loader   

  •         ArrayList list = new ArrayList();   

  •         // Add unpacked directories   

  •         if (unpacked != null) {   

  •             for (int i = 0; i < unpacked.length; i++)  {   
  •                 File file = unpacked;   

  •                 if (!file.isDirectory() || !file.exists() || !file.canRead())   

  •                     continue;   

  •                 if (debug >= 1)   

  •                     log("  Including directory " + file.getAbsolutePath());   

  •                 URL url = new URL("file"null,   
  •                                   file.getCanonicalPath() + File.separator);   
  •                 list.add(url.toString());   
  •             }   
  •         }   

  •         // Add packed directory JAR files   

  •         if (packed != null) {   

  •             for (int i = 0; i < packed.length; i++) {   
  •                 File directory = packed;   

  •                 if (!directory.isDirectory() || !directory.exists() ||   
  •                     !directory.canRead())   

  •                     continue;   
  •                 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]);   

  •                     if (debug >= 1)   

  •                         log("  Including jar file " + file.getAbsolutePath());   

  •                     URL url = new URL("file"null,   
  •                                       file.getCanonicalPath());   
  •                     list.add(url.toString());   
  •                 }   
  •             }   
  •         }   

  •         // Construct the class loader itself   

  •         String array[] = (String[]) list.toArray(new String[list.size()]);   


  •         StandardClassLoader classLoader = null;   

  •         if (parent == null)   

  •             classLoader = new StandardClassLoader(array);   

  •         else  

  •             classLoader = new StandardClassLoader(array, parent);   

  •         classLoader.setDelegate(true);   

  •         return (classLoader);   
  •     }  


  ClassLoaderFactory创建的是StandardClassLoader(org.apache.catalina.loader包中)
  由Bootstrap类调用Catalina类的process()方法,再调用execute()方法,再调用start()来启动Server实例。
  当Tomcat开启每个Context时,是调用的StandardContext的start()方法,其中:
  设置Loader为WebappLoader

view plaincopy to clipboardprint?





  • if (getLoader() == null) {      // (2) Required by Manager   

  •             if (getPrivileged()) {   

  •                 if (debug >= 1)   

  •                     log("Configuring privileged default Loader");   

  •                 setLoader(new WebappLoader(this.getClass().getClassLoader()));   

  •             } else {   

  •                 if (debug >= 1)   

  •                     log("Configuring non-privileged default Loader");   

  •                 setLoader(new WebappLoader(getParentClassLoader()));   
  •             }   
  •         }  


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

view plaincopy to clipboardprint?





  • private ClassLoader bindThread() {   
  •         ClassLoader oldContextClassLoader =    
  •             Thread.currentThread().getContextClassLoader();   

  •         if (getResources() == null)   

  •             return oldContextClassLoader;   
  •         Thread.currentThread().setContextClassLoader   
  •             (getLoader().getClassLoader());   
  •         DirContextURLStreamHandler.bind(getResources());   

  •         if (isUseNaming()) {   

  •             try {   

  •                 ContextBindings.bindThread(thisthis);   

  •             } catch (NamingException e) {   

  •                 // Silent catch, as this is a normal case during the early   

  •                 // startup stages   
  •             }   
  •         }   

  •         return oldContextClassLoader;   
  •     }  


  WebappLoader的ClassLoader的赋值如下:

view plaincopy to clipboardprint?





  • private String loaderClass =   

  •         "org.apache.catalina.loader.WebappClassLoader";   
  • ......   

  • public void start() throws LifecycleException {   

  •         // Validate and update our current component state   

  •         if (started)   

  •             throw new LifecycleException   

  •                 (sm.getString("webappLoader.alreadyStarted"));   

  •         if (debug >= 1)   

  •             log(sm.getString("webappLoader.starting"));   

  •         lifecycle.fireLifecycleEvent(START_EVENT, null);   

  •         started = true;   

  •         if (container.getResources() == null)   

  •             return;   

  •         // Register a stream handler factory for the JNDI protocol   
  •         URLStreamHandlerFactory streamHandlerFactory =   

  •             new DirContextURLStreamHandlerFactory();   

  •         try {   
  •             URL.setURLStreamHandlerFactory(streamHandlerFactory);   

  •         } catch (Throwable t) {   

  •             // Ignore the error here.   
  •         }   

  •         // Construct a class loader based on our current repositories list   

  •         try {   
  •             classLoader = createClassLoader();   
  •             classLoader.setResources(container.getResources());   

  •             classLoader.setDebug(this.debug);   

  •             classLoader.setDelegate(this.delegate);   

  •             if (container instanceof StandardContext)   
  •                 classLoader.setAntiJARLocking(((StandardContext) container).getAntiJARLocking());   

  •             for (int i = 0; i < repositories.length; i++) {   
  •                 classLoader.addRepository(repositories);   
  •             }   

  •             // Configure our repositories   
  •             setRepositories();   
  •             setClassPath();   
  •             setPermissions();   

  •             if (classLoader instanceof Lifecycle)   
  •                 ((Lifecycle) classLoader).start();   

  •             // Binding the Webapp class loader to the directory context   
  •             DirContextURLStreamHandler.bind   

  •                 ((ClassLoader) classLoader, this.container.getResources());   

  •         } catch (Throwable t) {   

  •             throw new LifecycleException("start: ", t);   
  •         }   

  •         // Validate that all required packages are actually available   
  •         validatePackages();   

  •         // Start our background thread if we are reloadable   

  •         if (reloadable) {   

  •             log(sm.getString("webappLoader.reloading"));   

  •             try {   
  •                 threadStart();   

  •             } catch (IllegalStateException e) {   

  •                 throw new LifecycleException(e);   
  •             }   
  •         }   
  •     }   
  • ......   

  • private WebappClassLoader createClassLoader()   

  •         throws Exception {   
  •         Class clazz = Class.forName(loaderClass);   

  •         WebappClassLoader classLoader = null;   

  •         if (parentClassLoader == null) {   

  •             // Will cause a ClassCast is the class does not extend WCL, but   

  •             // this is on purpose (the exception will be caught and rethrown)   
  •             classLoader = (WebappClassLoader) clazz.newInstance();   

  •         } else {   

  •             Class[] argTypes = { ClassLoader.class };   
  •             Object[] args = { parentClassLoader };   
  •             Constructor constr = clazz.getConstructor(argTypes);   
  •             classLoader = (WebappClassLoader) constr.newInstance(args);   
  •         }   

  •         return classLoader;   
  •     }  


  在WebappClassLoader中,其findClass的搜索顺序与一般的ClassLoader的搜索顺序不同。
  一般的ClassLoader的搜索顺序为:
  将其委托给父ClassLoader,如果父ClassLoader不能载入相应类,则才交给自己处理
  但是WebappClassLoader中,其是先由自己来处理,如果不行再委托给父ClassLoader
  相关源代码如下:

view plaincopy to clipboardprint?





  • Class clazz = null;   

  •        try {   

  •            if (debug >= 4)   

  •                log("      findClassInternal(" + name + ")");   

  •            try {   
  •                clazz = findClassInternal(name);   

  •            } catch(ClassNotFoundException cnfe) {   

  •                if (!hasExternalRepositories) {   

  •                    throw cnfe;   
  •                }   

  •            } catch(AccessControlException ace) {   
  •                ace.printStackTrace();   

  •                throw new ClassNotFoundException(name);   

  •            } catch (RuntimeException e) {   

  •                if (debug >= 4)   

  •                    log("      -->RuntimeException Rethrown", e);   

  •                throw e;   
  •            }   

  •            if ((clazz == null) && hasExternalRepositories) {   

  •                try {   

  •                    clazz = super.findClass(name);   

  •                } catch(AccessControlException ace) {   

  •                    throw new ClassNotFoundException(name);   

  •                } catch (RuntimeException e) {   

  •                    if (debug >= 4)   

  •                        log("      -->RuntimeException Rethrown", e);   

  •                    throw e;   
  •                }   
  •            }   

  •            if (clazz == null) {   

  •                if (debug >= 3)   

  •                    log("    --> Returning ClassNotFoundException");   

  •                throw new ClassNotFoundException(name);   
  •            }   

  •        } catch (ClassNotFoundException e) {   

  •            if (debug >= 3)   

  •                log("    --> Passing on ClassNotFoundException", e);   

  •            throw e;   
  •        }  


  以下引自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

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-330338-1-1.html 上篇帖子: tomcat配置ssl单向认证 下篇帖子: Tomcat 5可以通过
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表