`

Tomcat源码解读系列(四)——Tomcat类加载机制概述

 
阅读更多

摘自:http://lengyun3566.iteye.com/blog/1683972

 

声明:源码版本为Tomcat 6.0.35

         在本系列的第二篇文章中,曾经介绍过在Tomcat启动时会初始化类加载器(ClassLoader),来处理整个Web工程中Class的加载问题。

         类加载机制是Java平台中相当重要的核心技术,待笔者有所积累后会再次讨论这个话题。在一般的业务开发中我们可能较少接触和使用ClassLoader,但是在进行框架级程序开发时,设计良好的类加载机制能够实现更好地模块划分和更优的设计,如Java模块化技术OSGi就是通过为每个组件声明独立的类加载器来实现组件的动态部署功能。在Tomcat的代码实现中,为了优化内存空间以及不同应用间的类隔离,Tomcat通过内置的一些类加载器来完成了这些功能。

         在Java语言中,ClassLoader是以父子关系存在的,Java本身也有一定的类加载规范。在Tomcat中基本的ClassLoader层级关系如下图所示:



 在Tomcat启动的时候,会初始化图示所示的类加载器。而上面的三个类加载器:CommonClassLoader、CatalinaClassLoader和SharedClassLoader是与具体部署的Web应用无关的,而WebappClassLoader则对应Web应用,每个Web应用都会有独立的类加载器,从而实现类的隔离。

         我们首先来看Tomcat的初始化,在Bootstrap的init方法中,会调用initClassLoaders方法,该方法负责前图中前三个类加载器的初始化:

 

Java代码  收藏代码
  1. private void initClassLoaders() {  
  2.         try {  
  3.             //初始化CommonClassLoader  
  4.             commonLoader = createClassLoader("common"null);  
  5.             if( commonLoader == null ) {  
  6.                 commonLoader=this.getClass().getClassLoader();  
  7.             }  
  8. //初始化其它两个类加载器  
  9.             catalinaLoader = createClassLoader("server", commonLoader);  
  10.             sharedLoader = createClassLoader("shared", commonLoader);  
  11.         } catch (Throwable t) {  
  12.             log.error("Class loader creation threw exception", t);  
  13.             System.exit(1);  
  14.         }  
  15.     }  

 我们可以看到,此书初始化了三个类加载器,并且catalinaLoader和sharedLoader都以commonLoader作为父类加载器,在这个方法中,将核心的业务交给了createClassLoader方法来实现:

 

Java代码  收藏代码
  1. private ClassLoader createClassLoader(String name, ClassLoader parent)  
  2.         throws Exception {  
  3.         //读取配置属性,相关的配置属性在catalina.properties文件中  
  4.         String value = CatalinaProperties.getProperty(name + ".loader");  
  5.         //如果没有对应的配置,将不会创建新的类加载器,而是返回传入的父类加载器  
  6.         if ((value == null) || (value.equals("")))  
  7.             return parent;  
  8.   
  9.        //解析得到的配置文件,确定本ClassLoader要加载那些目录下的资源和JAR包等  
  10.         StringTokenizer tokenizer = new StringTokenizer(value, ",");  
  11.         while (tokenizer.hasMoreElements()) {  
  12.             String repository = tokenizer.nextToken();  
  13.   
  14.           //此处省略的代码为将配置文件中的${catalina.base}、${catalina.home}等变量转  
  15. //换为绝对路径  
  16.   
  17.         //格式化得到的位置路径和类型  
  18.         String[] locations = (String[]) repositoryLocations.toArray(new String[0]);  
  19.         Integer[] types = (Integer[]) repositoryTypes.toArray(new Integer[0]);  
  20.  //生成真正的类加载器   
  21.         ClassLoader classLoader = ClassLoaderFactory.createClassLoader  
  22.             (locations, types, parent);  
  23.   
  24.        //以下的代码为将生成的类加载器注册为MBean  
  25.   
  26.         return classLoader;  
  27.   
  28.     }  

 而每个类加载器所加载的路径或JAR是在catalina.properties文件中定义的,默认的配置如下:

 

Java代码  收藏代码
  1. common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar  
  2. server.loader=  
  3. shared.loader=  

 按照默认的配置,catalinaLoadersharedLoader的配置项为空,因此不会创建对应的ClassLoader,而只会创建CommonClassLoader,该类加载器对应的Java实现类为:org.apache.catalina.loader. StandardClassLoader,该类继承自org.apache.catalina.loader. URLClassLoader,有关Tomcat基础类都会有该类加载器加载。例如在Bootstrapinit方法中,会调用Catalina类的init方法来完成相关操作:

 

Java代码  收藏代码
  1. public void init() throws Exception{  
  2.   
  3.        //将当前线程的类加载器设置为catalinaLoader  
  4.         Thread.currentThread().setContextClassLoader(catalinaLoader);  
  5.   
  6.         SecurityClassLoad.securityClassLoad(catalinaLoader);  
  7.   
  8.        //使用catalinaLoader来加载Catalina类  
  9.         Class startupClass =  
  10.             catalinaLoader.loadClass  
  11.             ("org.apache.catalina.startup.Catalina");  
  12.         Object startupInstance = startupClass.newInstance();  
  13.   
  14.         //调用Catalina的setParentClassLoader方法,设置为sharedLoader  
  15.         String methodName = "setParentClassLoader";  
  16.         Class paramTypes[] = new Class[1];  
  17.         paramTypes[0] = Class.forName("java.lang.ClassLoader");  
  18.         Object paramValues[] = new Object[1];  
  19.         paramValues[0] = sharedLoader;  
  20.         Method method =  
  21.             startupInstance.getClass().getMethod(methodName, paramTypes);  
  22.         method.invoke(startupInstance, paramValues);  
  23.   
  24.         catalinaDaemon = startupInstance;  
  25.     }  

 以上为基础的三个类加载器的初始化过程。在每个Web应用初始化的时候,StandardContext对象代表每个Web应用,它会使用WebappLoader类来加载Web应用,而WebappLoader中会初始化org.apache.catalina.loader. WebappClassLoader来为每个Web应用创建单独的类加载器,在上一篇文章中,我们介绍过,当处理请求时,容器会根据请求的地址解析出由哪个Web应用来进行对应的处理,进而将当前线程的类加载器设置为请求Web应用的类加载器。让我们看一下WebappClassLoader的核心方法,也就是loadClass:

 

Java代码  收藏代码
  1. public synchronized Class loadClass(String name, boolean resolve)  
  2.         throws ClassNotFoundException {  
  3.   
  4.         Class clazz = null;  
  5.   
  6.         //首先检查已加载的类  
  7.         // (0) Check our previously loaded local class cache  
  8.         clazz = findLoadedClass0(name);  
  9.         if (clazz != null) {  
  10.             if (log.isDebugEnabled())  
  11.                 log.debug("  Returning class from cache");  
  12.             if (resolve)  
  13.                 resolveClass(clazz);  
  14.             return (clazz);  
  15.         }  
  16.   
  17.         // (0.1) Check our previously loaded class cache  
  18.         clazz = findLoadedClass(name);  
  19.         if (clazz != null) {  
  20.             if (log.isDebugEnabled())  
  21.                 log.debug("  Returning class from cache");  
  22.             if (resolve)  
  23.                 resolveClass(clazz);  
  24.             return (clazz);  
  25.         }  
  26.   
  27.         // (0.2) Try loading the class with the system class loader, to prevent  
  28.         //       the webapp from overriding J2SE classes  
  29.         try {  
  30.             clazz = system.loadClass(name);  
  31.             if (clazz != null) {  
  32.                 if (resolve)  
  33.                     resolveClass(clazz);  
  34.                 return (clazz);  
  35.             }  
  36.         } catch (ClassNotFoundException e) {  
  37.             // Ignore  
  38.         }  
  39.   
  40.         // (0.5) Permission to access this class when using a SecurityManager  
  41.         if (securityManager != null) {  
  42.             int i = name.lastIndexOf('.');  
  43.             if (i >= 0) {  
  44.                 try {  
  45.                     securityManager.checkPackageAccess(name.substring(0,i));  
  46.                 } catch (SecurityException se) {  
  47.                     String error = "Security Violation, attempt to use " +  
  48.                         "Restricted Class: " + name;  
  49.                     log.info(error, se);  
  50.                     throw new ClassNotFoundException(error, se);  
  51.                 }  
  52.             }  
  53.         }  
  54.   
  55.         boolean delegateLoad = delegate || filter(name);  
  56.         //Tomcat允许按照配置来确定优先使用本Web应用的类加载器加载还是使用父类  
  57. //加载器来进行类加载,此处先使用父类加载器进行加载  
  58.         // (1) Delegate to our parent if requested  
  59.         if (delegateLoad) {  
  60.             if (log.isDebugEnabled())  
  61.                 log.debug("  Delegating to parent classloader1 " + parent);  
  62.             ClassLoader loader = parent;  
  63.             if (loader == null)  
  64.                 loader = system;  
  65.             try {  
  66.                 clazz = loader.loadClass(name);  
  67.                 if (clazz != null) {  
  68.                     if (log.isDebugEnabled())  
  69.                         log.debug("  Loading class from parent");  
  70.                     if (resolve)  
  71.                         resolveClass(clazz);  
  72.                     return (clazz);  
  73.                 }  
  74.             } catch (ClassNotFoundException e) {  
  75.                 ;  
  76.             }  
  77.         }  
  78.         //使用本地的类加载器进行加载  
  79.         // (2) Search local repositories  
  80.         if (log.isDebugEnabled())  
  81.             log.debug("  Searching local repositories");  
  82.         try {  
  83.             clazz = findClass(name);  
  84.             if (clazz != null) {  
  85.                 if (log.isDebugEnabled())  
  86.                     log.debug("  Loading class from local repository");  
  87.                 if (resolve)  
  88.                     resolveClass(clazz);  
  89.                 return (clazz);  
  90.             }  
  91.         } catch (ClassNotFoundException e) {  
  92.             ;  
  93.         }  
  94.         //如果没有特殊配置的话,使用父类加载器加载类  
  95.         // (3) Delegate to parent unconditionally  
  96.         if (!delegateLoad) {  
  97.             if (log.isDebugEnabled())  
  98.                 log.debug("  Delegating to parent classloader at end: " + parent);  
  99.             ClassLoader loader = parent;  
  100.             if (loader == null)  
  101.                 loader = system;  
  102.             try {  
  103.                 clazz = loader.loadClass(name);  
  104.                 if (clazz != null) {  
  105.                     if (log.isDebugEnabled())  
  106.                         log.debug("  Loading class from parent");  
  107.                     if (resolve)  
  108.                         resolveClass(clazz);  
  109.                     return (clazz);  
  110.                 }  
  111.             } catch (ClassNotFoundException e) {  
  112.                 ;  
  113.             }  
  114.         }  
  115.         //若最终类还是没有找到,抛出异常  
  116.         throw new ClassNotFoundException(name);  
  117.   
  118.     }  

 以上就是Web应用中类加载的机制。在默认情况下,WebappClassLoader的父类加载器就是CommonClassLoader,但是我们可以通过修改catalina.properties文件来设置SharedClassLoader,从而实现多个Web应用共用类库的效果。

tomcat context.xml文件配置<Loader delegate="true" />设置WebappClassLoader类是否委托父类CommonClassLoader加载,默认为false 不委托父类加载。

分享到:
评论

相关推荐

    大、小断层矿井小波SVM融合智能故障预测matlab代码.zip

    1.版本:matlab2014/2019a/2021a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    垂直SeekBar(拖动条).zip

    android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台

    libADLMIDI1-1.5.0-bp153.1.1.x86-64.rpm

    libADLMIDI1-1.5.0-bp153.1.1.x86_64.rpm 是用于在 x86_64 架构的设备上安装的 RPM 包,具体功能如下: 名称:libADLMIDI1 版本:1.5.0 摘要:带有 OPL3 (YMF262) 模拟器的软件 MIDI 合成器库 许可证:GPL-3.0-only 和 LGPL-3.0-only 该库提供了一个基于 ADLMIDI 的软件 MIDI 合成器,它模拟了 OPL3 音源芯片(FM 合成)。它可以通过使用 ADLMIDI 库来实现多平台的 MIDI 播放和 OPL3 模拟。 该 RPM 包适用于 x86_64 架构,用于在相关设备上安装 libADLMIDI1 库文件。库文件包括: /usr/lib64/libADLMIDI.so.1 和 /usr/lib64/libADLMIDI.so.1.5.0:库文件 /usr/share/doc/packages/libADLMIDI1/AUTHORS、/usr/share/doc/packages/libADLMIDI1/README.md 等文档文件:文档文件

    基于qt+C++实现u盘插拔检测.+源码(毕业设计&课程设计&项目开发)

    基于qt+C++实现u盘插拔检测.+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于qt+C++实现u盘插拔检测.+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于qt+C++实现u盘插拔检测.+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于qt+C++实现u盘插拔检测.+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~

    Quectel_Product_Brochure_CN_V7.9.pdf

    Quectel_Product_Brochure_CN_V7.9.pdf

    更换软件主题(apk方式).zip

    android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台

    chepai-reg-main (2).zip

    phpstudy

    Python 入门详细教程-1天学会 Python.docx

    python入门

    二维码扫描的实现.zip

    android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台

    移动机器人机械臂的设计开题报告.doc

    移动机器人机械臂的设计开题报告.doc

    基于QT+C++开发的智能平台访客系统+源码

    用法链接:https://menghui666.blog.csdn.net/article/details/137977678?spm=1001.2014.3001.5502 基于QT+C++开发的智能平台访客系统+源码,包含主界面、系统设置、警情查询、调试帮助、用户退出功能。 基于QT+C++开发的智能平台访客系统+源码,包含主界面、系统设置、警情查询、调试帮助、用户退出功能。 基于QT+C++开发的智能平台访客系统+源码,包含主界面、系统设置、警情查询、调试帮助、用户退出功能。

    三菱机械臂校点说明.pptx

    三菱机械臂校点说明.pptx

    按字母索引滑动.zip

    android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台

    激光推送客户端demo.zip

    android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台

    c语言入门,小白进军C语言.zip

    C语言诞生于美国的贝尔实验室,由丹尼斯·里奇(Dennis MacAlistair Ritchie)以肯尼斯·蓝·汤普森(Kenneth Lane Thompson)设计的B语言为基础发展而来,在它的主体设计完成后,汤普森和里奇用它完全重写了UNIX,且随着UNIX的发展,c语言也得到了不断的完善。为了利于C语言的全面推广,许多专家学者和硬件厂商联合组成了C语言标准委员会,并在之后的1989年,诞生了第一个完备的C标准,简称“C89”,也就是“ANSI C”,截至2020年,最新的C语言标准为2018年6月发布的“C18”。 [5] C语言之所以命名为C,是因为C语言源自Ken Thompson发明的B语言,而B语言则源自BCPL语言。 1967年,剑桥大学的Martin Richards对CPL语言进行了简化,于是产生了BCPL(Basic Combined Programming Language)语言。

    Python入门到精通.zip

    python入门 单元测试和测试用例 Python标准库中的模块unittest提供了代码测试工具。 单元测试用于核实函数的某个防霾呢没有问题; 测试用例是一组单元测试,这些单元测试仪器一起核实函数在各种情形下的行为都符合要求。良好的测试用例考虑到了函数可能收到的各种收入,包含所有针对这些情形的测试。 全覆盖式测试用例包含一整套单元测试,涵盖了各种可能的函数使用方式。 对于大型项目,要实现全覆盖可能很难。通常,最初只要对针对代码的重要行为编写测试即可,等项目给广泛使用时再考虑全覆盖。 可通过的测试 创建测试用例的语法需要一段时间才能习惯,但测试用例创建后,再添加针对函数的单元测试就很简单了。要为函数编写测试用例,可先导入模块unittest以及要测试的函数,在创建一个继承unittest.TestCase的类,并编写一系列方法对函数行为的不同方面进行测试。 下面test_name_function.py一个只包含一个方法的测试用例,它检查函数get_formatted_name()在给定名和姓时能否正确的工作。

    基于matlabbenders分解算法.zip

    基于matlabbenders分解算法.zip

    dsp工程设计讲座.ppt

    dsp工程设计讲座.ppt

    Adams空间复杂机械臂动力学仿真研究.doc

    Adams空间复杂机械臂动力学仿真研究.doc

    基于Android+OpenCV的车牌识别系统源码+使用文档+全部资料(优秀项目).zip

    【资源说明】 基于Android+OpenCV的车牌识别系统源码+使用文档+全部资料(优秀项目).zip基于Android+OpenCV的车牌识别系统源码+使用文档+全部资料(优秀项目).zip基于Android+OpenCV的车牌识别系统源码+使用文档+全部资料(优秀项目).zip 【备注】 1、该项目是个人高分毕业设计项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 3、本项目适合计算机相关专业(如软件工程、计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载使用,也可作为毕业设计、课程设计、作业、项目初期立项演示等,当然也适合小白学习进阶。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!

Global site tag (gtag.js) - Google Analytics