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

[经验分享] Tomcat源码分析(八)--载入器

[复制链接]

尚未签到

发表于 2015-8-11 07:27:18 | 显示全部楼层 |阅读模式
本系列转载自 http://blog.iyunv.com/haitao111313/article/category/1179996    在讲Tomcat的载入器之前,先要了解一下java的类加载机制,这里就不具体说了,仅仅写一点我认为比较重要的东西:
  1:一般实现自己的类加载器是重写ClassLoader的findClass方法,然后在这个方法里面读取class文件为byte[]数组,传入defineClass方法,defineClass方法返回我们加载的类。这样便实现了我们自己的简单的类加载器。下面是一个简单的自定义类加载器的findClass方法:
  



[java] view plaincopyprint?

  • protected Class<?> findClass(String name) throws ClassNotFoundException {   
  •         byte[] classData = getClassData(name); //getClassData方法是通过class文件的名字得到一个字节数组  
  •         if (classData == null) {   
  •             throw new ClassNotFoundException();   
  •         }   
  •         else {   
  •             return defineClass(name, classData, 0, classData.length); //加载进jvm中。  
  •         }   
  •     }   

  
  问题是: 可不可以重写ClassLoader的loadClass方法来实现自己的类加载器? 答案是可以,Tomcat就是用的这种方法。jdk建议我们实现自己的类加载器的时候是重写findClass方法,不建议重写loadclass方法,因为ClassLoader的loadclass方法保证了类加载的父亲委托机制,如果重写了这个方法,就意味着需要实现自己在重写的loadclass方法实现父亲委托机制,下面看看ClassLoader的loadclass方法,看怎么实现父亲委托机制的:
  



[java] view plaincopyprint?

  • protected synchronized Class<?> loadClass(String name, boolean resolve)  
  • throws ClassNotFoundException  
  •    {  
  • // First, check if the class has already been loaded  
  • Class c = findLoadedClass(name);//检查该类是否已经被加载了  
  • if (c == null) {  
  •     try {  
  •     if (parent != null) {//如果父加载器不为空就用父加载器加载该类。  
  •         c = parent.loadClass(name, false);  
  •     } else {  
  •         c = findBootstrapClass0(name);  
  •     }  
  •     } catch (ClassNotFoundException e) {  
  •         // If still not found, then invoke findClass in order  
  •         // to find the class.  
  •         c = findClass(name);//在自己实现的类加载器中重写这个findClass方法。  
  •     }  
  • }  
  • if (resolve) {  
  •     resolveClass(c);  
  • }  
  • return c;  
  •    }  
这样就保证了父亲委托机制。当所有父类都加载不了,才会调用findClass方法,即调用到我们自己的类加载器的findClass方法。以此实现类加载。  
  2:在我们自己实现的类加载器中,defineClass方法才是真正的把类加载进jvm,defineClass是从ClassLoader继承而来,把一个表示类的字节数组加载进jvm转换为一个类。

  3:我们自己实现的类加载器跟系统的类加载器没有本质的区别,最大的区别就是加载的路径不同,系统类加载器会加载环境变量CLASSPATH中指明的路径和jvr文件,我们自己的类加载器可以定义自己的需要加载的类文件路径.同样的一个class文件,用系统类加载器和自定义加载器加载进jvm后类的结构是没有区别的,只是他们访问的权限不一样,生成的对象因为加载器不同也会不一样.当然我们自己的类加载器可以有更大的灵活性,比如把一个class文件(其实就是二进制文件)加密后(简单的加密就把0和1互换),系统类加载器就不能加载,需要由我们自己定义解密类的加载器才能加载该class文件.
  现在来初步的看看Tomcat的类加载器,为什么Tomcat要有自己的类加载器.这么说吧,假如没有自己的类加载器,我们知道,在一个Tomcat中是可以部署很多应用的,如果所有的类都由系统类加载器来加载,那么部署在Tomcat上的A应用就可以访问B应用的类,这样A应用与B应用之间就没有安全性可言了。还有一个原因是因为Tomcat需要实现类的自动重载,所以也需要实现自己的类加载器。Tomcat的载入器是实现了Loader接口的WebappLoader类,也是Tomcat的一个组件,实现Lifecycle接口以便统一管理,启动时调用start方法,在start方法主要做了以下的事情:
  1:创建一个真正的类加载器以及设置它加载的路径,调用createClassLoader方法
  2:启动一个线程来支持自动重载,调用threadStart方法
  看createClassLoader方法:
  



[java] view plaincopyprint?

  • 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;  
  •   
  •    }  

  在createClassLoader方法内,实例化了一个真正的类加载器WebappClassLoader,代码很简单。WebappClassLoader是Tomcat的类加载器,它继承了URLClassLoader(这个类是ClassLoader的孙子类)类。重写了findClass和loadClass方法。Tomcat的findClass方法并没有加载相关的类,只是从已经加载的类中查找这个类有没有被加载,具体的加载是在重写的loadClass方法中实现,从上面的对java的讨论可知,重写了loadClass方法就意味着失去了类加载器的父亲委托机制,需要自己来实现父亲委托机制。Tomcat正是这么做的,下面看WebappClassLoader的loadClass方法:
  



[java] view plaincopyprint?

  • public Class loadClass(String name, boolean resolve)  
  •         throws ClassNotFoundException {  
  •         if (debug >= 2)  
  •             log("loadClass(" + name + ", " + resolve + ")");  
  •         Class clazz = null;  
  •   
  •         // Don't load classes if class loader is stopped  
  •         if (!started) {  
  •             log("Lifecycle error : CL stopped");  
  •             throw new ClassNotFoundException(name);  
  •         }  
  •   
  •         // (0) Check our previously loaded local class cache  
  •         //查找本地缓存是否已经加载了该类  
  •         clazz = findLoadedClass0(name);  
  •         if (clazz != null) {  
  •             if (debug >= 3)  
  •                 log("  Returning class from cache");  
  •             if (resolve)  
  •                 resolveClass(clazz);  
  •             return (clazz);  
  •         }  
  •   
  •         // (0.1) Check our previously loaded class cache  
  •         
  •         clazz = findLoadedClass(name);  
  •         if (clazz != null) {  
  •             if (debug >= 3)  
  •                 log("  Returning class from cache");  
  •             if (resolve)  
  •                 resolveClass(clazz);  
  •             return (clazz);  
  •         }  
  •   
  •         // (0.2) Try loading the class with the system class loader, to prevent  
  •         //       the webapp from overriding J2SE classes  
  •         try {  
  •             clazz = system.loadClass(name);// (0.2) 先检查系统ClassLoader,因此WEB-INF/lib和WEB-INF/classes或{tomcat}/libs下的类定义不能覆盖JVM 底层能够查找到的定义(譬如不能通过定义java.lang.Integer替代底层的实现  
  •             if (clazz != null) {  
  •                 if (resolve)  
  •                     resolveClass(clazz);  
  •                 return (clazz);  
  •             }  
  •         } catch (ClassNotFoundException e) {  
  •             // Ignore  
  •         }  
  •   
  •        ...............................................  
  • //这是一个很奇怪的定义,JVM的ClassLoader建议先由parent去load,load不到自己再去load(见如上 ClassLoader部分),而Servelet规范的建议则恰好相反,Tomcat的实现则做个折中,由用户去决定(context的 delegate定义),默认使用Servlet规范的建议,即delegate=false  
  •         boolean delegateLoad = delegate || filter(name);  
  • // (1) 先由parent去尝试加载,此处的parent是SharedClassLoader,见如上说明,如上说明,除非设置了delegate,否则这里不执行  
  •         // (1) Delegate to our parent if requested  
  •         if (delegateLoad) {  
  •             if (debug >= 3)  
  •                 log("  Delegating to parent classloader");  
  •             ClassLoader loader = parent;  
  •             if (loader == null)//此处parent是否为空取决于context 的privileged属性配置,默认privileged=true,即parent为SharedClassLoader  
  •                 loader = system;  
  •             try {  
  •                 clazz = loader.loadClass(name);  
  •                 if (clazz != null) {  
  •                     if (debug >= 3)  
  •                         log("  Loading class from parent");  
  •                     if (resolve)  
  •                         resolveClass(clazz);  
  •                     return (clazz);  
  •                 }  
  •             } catch (ClassNotFoundException e) {  
  •                 ;  
  •             }  
  •         }  
  •   
  •        // 到WEB-INF/lib和WEB-INF/classes目录去搜索,细节部分可以再看一下findClass,会发现默认是先搜索WEB-INF/classes后搜索WEB-INF/lib  
  •         if (debug >= 3)  
  •             log("  Searching local repositories");  
  •         try {  
  •             clazz = findClass(name);  
  •             if (clazz != null) {  
  •                 if (debug >= 3)  
  •                     log("  Loading class from local repository");  
  •                 if (resolve)  
  •                     resolveClass(clazz);  
  •                 return (clazz);  
  •             }  
  •         } catch (ClassNotFoundException e) {  
  •             ;  
  •         }  
  •   
  •         // (3) Delegate to parent unconditionally  
  •         if (!delegateLoad) {  
  •             if (debug >= 3)  
  •                 log("  Delegating to parent classloader");  
  •             ClassLoader loader = parent;  
  •             if (loader == null)  
  •                 loader = system;  
  •             try {  
  •                 clazz = loader.loadClass(name);  
  •                 if (clazz != null) {  
  •                     if (debug >= 3)  
  •                         log("  Loading class from parent");  
  •                     if (resolve)  
  •                         resolveClass(clazz);  
  •                     return (clazz);  
  •                 }  
  •             } catch (ClassNotFoundException e) {  
  •                 ;  
  •             }  
  •         }  
  •   
  •         // This class was not found  
  •         throw new ClassNotFoundException(name);  
  •   
  •     }  

上面的代码很长,但实现了Tomcat自己的类加载机制,具体的加载规则是:  
  1:因为所有已经载入的类都会缓存起来,所以先检查本地缓存
  2:如本地缓存没有,则检查上一级缓存,即调用ClassLoader类的findLoadedClass()方法;
  3:若两个缓存都没有,则使用系统的类进行加载,防止Web应用程序中的类覆盖J2EE的类
  4:若打开标志位delegate(表示是否代理给父加载器加载),或者待载入的类是属于包触发器的包名,则调用父类载入器来加载,如果父类载入器是null,则使用系统类载入器
  5:从当前仓库中载入相关类
  6:若当前仓库中没有相关类,且标志位delegate为false,则调用父类载入器来加载,如果父类载入器是null,则使用系统类载入器(4跟6只能执行一个步骤的)
  
  这里有有一点不明白的是在第三步使用系统类加载若失败后,在第四步和第六步就没必要但父类载入器为null的时候再用系统类载入器来载入了???有谁明白的麻烦留个言,不胜感激~
  参考了一下http://ayufox.iteye.com/blog/631190这篇博文.

运维网声明 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-97156-1-1.html 上篇帖子: tomcat 7.0 配置详解 下篇帖子: Linux 启动tomcat服务器2
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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