`
cuiyi.crazy
  • 浏览: 52464 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

部署ear包出错引发的ClassLoader的思考

阅读更多
应用服务器常常包含多个容器,当前使用的是JBoss,在部署ear包的时候,遇到了一些比较有意思的问题,遂随着不断的推敲,从而解决了问题,也对classloader在应用服务器如JBoss中有了一点的推测(不当之处请光顾的朋友指出)。
测试环境:JBoss4.0.5.GA 、Gentoo Linux、 spring、ejb(ear工程)

1)使用ant打包脚本的疏忽,把struts action的class同时放在了${ear_file}/${jar_file} 和${ear_file}/${war_file}/WEB-INF/{lib}/${jar_file}
      纠正之后,再次修改了struts action的实现类,后者确实不断地更新,但是始终未被执行,而执行的总是前者

2)ant打包,把${xml_config_file}放在了${ear_file}/${jar_file} 和${ear_file}/${war_file}/WEB-INF/classes/${xml_config_file}
      之后做了如下的测试:
      21)前者不变,更新后者,结果:取新增加的物件出错
      22)移除前者,更新后者,结果:可以取到新增加的物件
      23)保持前者,新物件的配置作为一个新的文件,同时也放在后者的位置,结果:可以取到新增加的物件。

3)通过IoC注入配置文件的位置,然后读取配置文件的内容(未使用Spring的解析方法,而是自己实现解析):
  注入xml位置的配置如下(粗体处):
<bean id="test.DataMigrateCenter" class="demo.service.DataMigrateCenter">
        <property name="dataExtractDao"><ref bean="demo.dataExtractDao"/></property>
        <property name="markExtractedDao"><ref bean="demo.markExtractedDao"/></property>
        <property name="errorsPath"   value="/home/demo/Errors/"/>
        <property name="invoicesPath" value="/home/demo/Invoices/"/>
        <property name="archivesPath" value="/home/demo/Archives/"/>
        
        [b]<property name="sqlPath"      value="x.war/WEB-INF/classes/xyz_sql.xml"/>[/b]
</bean>


xyz_sql.xml的真实位置在/home/jboss/jboss-4.0.5.GA/server/xyz/deploy/x.ear/x.war/WEB-INF/classes/xyz_sql.xml

注入了sqlPath后,交给了一个工具类来解析,这个工具类放在表现层,即打包到war里,代码类似如下
private static Document getRootDocument(String fileName)  throws DocumentException{//参数fileName即注入的sqlPath
        SAXReader reader = new SAXReader();
       //Print Code
        InputStream in = SqlReaderHelper.class.getClassLoader().getResourceAsStream(fileName);
        Document document = reader.read(in);
        return document;
}


在getRootDocument方法的Print Code处,增加如下打印语句:
System.out.println(SqlReaderHelper.class.getClassLoader().getResource("/"));
System.out.println(SqlReaderHelper.class.getClassLoader().getResource(""));
System.out.println(SqlReaderHelper.class.getClassLoader().getResource("/test.xml"));
System.out.println(SqlReaderHelper.class.getClassLoader().getResource("/../test.xml"));
System.out.println(SqlReaderHelper.class.getClassLoader().getResource("../test.xml"));
System.out.println(Thread.currentThread().getContextClassLoader().getResource("/"));
System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));
System.out.println(SqlReaderHelper.class.getClass().getClassLoader().getResource(""));

其中,使用的test.xml实际上并不存在;
得到的输出结果(外加了打印语句的本身描述)
17:47:18,213 INFO  [STDOUT] -------->>>>>>>>SqlReaderHelper.class.getClassLoader().getResource("/") : null
17:47:18,214 INFO  [STDOUT] -------->>>>>>>>SqlReaderHelper.class.getClassLoader().getResource("") : file:/home/jboss/jboss-4.0.5.GA/server/xyz/deploy/x.ear/
17:47:18,222 INFO  [STDOUT] -------->>>>>>>>SqlReaderHelper.class.getClassLoader().getResource("/test.xml") : null
17:47:18,231 INFO  [STDOUT] -------->>>>>>>>SqlReaderHelper.class.getClassLoader().getResource("/../test.xml") : null
17:47:18,241 INFO  [STDOUT] -------->>>>>>>>SqlReaderHelper.class.getClassLoader().getResource("../test.xml") : null
17:47:18,241 INFO  [STDOUT]-------->>>>>>>>Thread.currentThread().getContextClassLoader().getResource("/"):null
17:47:18,242 INFO  [STDOUT]-------->>>>>>>>Thread.currentThread().getContextClassLoader().getResource(""):file:/home/jboss/jboss-4.0.5.GA/server/xyz/deploy/x.ear/
执行到System.out.println(Thread.currentThread().getContextClassLoader().getResource("")); 出错

将test.xml换成一个真实存在的文件 test.jar
并在getRootDocument方法的Print Code处,增加如下打印语句:
System.out.println(SqlReaderHelper.class.getClassLoader().getResource("/"));
System.out.println(SqlReaderHelper.class.getClassLoader().getResource(""));
System.out.println(SqlReaderHelper.class.getClassLoader().getResource("/test.jar"));
System.out.println(SqlReaderHelper.class.getClassLoader().getResource("/../test.jar"));
System.out.println(SqlReaderHelper.class.getClassLoader().getResource("../test.jar"));
System.out.println(SqlReaderHelper.class.getClassLoader().getResource("test.jar"));
System.out.println(Thread.currentThread().getContextClassLoader().getResource("/"));
System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));
System.out.println(SqlReaderHelper.class.getClassLoader().getResource(fileName));

得到的输出结果(外加了打印语句的本身描述)
17:57:16,882 INFO  [STDOUT]
-------->>>>>>>SqlReaderHelper.class.getClassLoader().getResource("/") : null
17:57:16,900 INFO  [STDOUT] -------->>>>>>>SqlReaderHelper.class.getClassLoader().getResource("") : file:/home/jboss/jboss-4.0.5.GA/server/xyz/deploy/x.ear/
17:57:16,909 INFO  [STDOUT] -------->>>>>>>SqlReaderHelper.class.getClassLoader().getResource("/test.jar") : null
17:57:16,918 INFO  [STDOUT] -------->>>>>>>SqlReaderHelper.class.getClassLoader().getResource("/../test.jar") : null
17:57:16,926 INFO  [STDOUT] -------->>>>>>>SqlReaderHelper.class.getClassLoader().getResource("../test.jar") : null
17:57:16,926 INFO  [STDOUT] -------->>>>>>>SqlReaderHelper.class.getClassLoader().getResource("test.jar") : file:/home/jboss/jboss-4.0.5.GA/server/xyz/deploy/x.ear/test.jar
17:57:16,927 INFO  [STDOUT]-------->>>>>>>Thread.currentThread().getContextClassLoader().getResource("/"):null
17:57:16,927 INFO  [STDOUT]------->>>>>>>Thread.currentThread().getContextClassLoader().getResource(""):file:/home/jboss/jboss-4.0.5.GA/server/xyz/deploy/x.ear/
17:57:16,927 INFO  [STDOUT] -------->>>>>>>SqlReaderHelper.class.getClassLoader().getResource("fileName") : file:/home/jboss/jboss-4.0.5.GA/server/xyz/deploy/x.ear/cxc3.war/WEB-INF/classes/cxc2sap_sql.xml


通过上述描述,可以简单的得出一些推论:
对1)2)
在应用服务器如JBoss中,加载${ear_file}/${jar_file}的EJB容器 和 加载${ear_file}/${war_file}的Web容器间存在一定的关系,根据ClassLoader的加载机制:当当前类加载器需要加载一个类的时候,首先请求父级的类加载器加载,如果父级加载器无法找到要加载的类(每个加载器仅仅在自己本身的classpath寻找要加载的类),才由当前类加载器来加载,如果加载不到就报错。
根据这个,可认为EJB容器的ClassLoader起了Web容器的父级ClassLoader的作用,即:请求加载一个action class时,当前类加载器是web容器,但是web容器的ClassLoader委托其父级加载器来加载,结果其父亲加载并加载成功了,所以不再加载本来正确的${war_file}/WEB-INF/lib or ${war_file}/WEB-INF/classes下的真正的类了

对3)
这些输出信息则更充分的证明了当使用${Class_name}.class.getClassLoader()的时候,真正起作用的类加载器便是父级类加载器,即使EJB容器的ClassLoader,从而得到的当前classpath是${ear_file}的路径。

回想过去经历:
基于这些实验,记得曾经遇到这样的错误:把struts.jar也放在了${ear_file}之下,运行报错误。
原因依然是类加载器的两个基本原理:
1)加载的委托机制,见上面的分析
2)当一个类被某一个ClassLoader加载后,与其相关的类都由同一个ClassLoader加载
于是得出如下结论:EJB容器加载了struts.jar,当web容器的ClassLoader加载自己的action class的实现类的时候,需要Action基类,但是根据默认的加载原理,关联的类应该由同一个类加载器完成,现在Action基类被父级的加载器加载(相对于当前),Action的实现类在当前的类加载器,故此发生错误。
分享到:
评论
1 楼 yeshucheng 2008-03-18  
有点意思,头晕!呵呵

相关推荐

    定义ClassLoader调用外部jar包

    自定义ClassLoader可能会引发安全异常,因为它可以加载不受信任的代码。因此,在生产环境中,确保对加载的类进行适当的权限检查是必要的。此外,为了保证多线程环境下的正确性,可能需要同步加载过程。 总之,...

    ClassLoader原理

    ClassLoader是一个抽象的概念,它是Java中的一个接口,位于`java.lang.ClassLoader`包下。它的主要职责是找到类的二进制数据(.class文件),然后将其转换为Class对象,以便JVM能够执行。Java的类加载过程遵循双亲...

    解决classloader的jar包

    包括commons-logging commons-beanutils commons-lang ezmorph json-lib-2.4-jdk15 commons-collections-3.2.1的jar包,可以解决 org/apache/commons/lang/exception/NestableRuntimeException的问题

    ClassLoader运行机制 自己写的

    - Ear ClassLoader:加载EAR应用的全局库(如lib目录下的JAR文件)。 - War ClassLoader:加载WAR应用的类,包括WEB-INF/classes和WEB-INF/lib下的JAR文件。 - EJB ClassLoader:加载EJB模块的类。 在类加载过程...

    classloader做的一个热部署

    eclipse工程格式 博文链接:https://aga.iteye.com/blog/200818

    classloader 热部署

    博文链接:https://kuangbaoxu.iteye.com/blog/206472

    Java ClassLoader定制实例

    在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中。理解ClassLoader的工作原理以及如何定制它,对于深入学习Java的运行机制和进行高级应用开发具有重要意义。本篇文章将...

    classloader

    Java ClassLoader是Java运行时系统的关键但经常被忽视的组件,负责在运行时查找和加载类文件。通过创建自定义ClassLoader,你可以定制JVM,使类文件的引入方式完全重新定义,这提供了很多实用和有趣的可能。这篇教程...

    破解java加密的ClassLoader.java,在classloader植入破解代码

    破解java加密的ClassLoader.java,在classloader植入破解代码

    ClassLoader小例子

    在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中,使得程序能够执行。本示例"ClassLoader小例子"将深入探讨这个概念,并通过一个具体的程序来演示其工作原理。下面我们...

    webshpere classloader 原理

    ### WebSphere ClassLoader原理 #### 一、概述 在探讨WebSphere Application Server v6中的ClassLoaders之前,我们首先简要回顾一下ClassLoaders的基本概念及其在Java虚拟机(JVM)中的作用。 **ClassLoaders**是...

    ClassLoader

    ### Java虚拟机中ClassLoader概述与双亲委托机制详解 #### 一、ClassLoader概念与作用 在Java编程语言中,`ClassLoader`是一个非常重要的组件,它负责加载程序运行所需的类文件到Java虚拟机(JVM)中。`ClassLoader`...

    自定义classloader的使用

    在Java中,Classloader是加载类的关键组件,它负责查找、加载和初始化字节码文件。自定义Classloader允许开发者根据特定需求定制类的加载逻辑,例如加密类文件、隔离不同版本的库或者动态加载代码。本文将深入探讨...

    ClassLoader 案例

    自定义ClassLoader允许开发者根据特定需求加载类,比如动态加载或更新类文件,这在某些高级应用场景中非常有用,如插件系统、热部署等。本案例将深入探讨如何创建一个自定义的ClassLoader,利用Java反射和注解技术...

    ClassLoader 详解.doc

    自定义ClassLoader是Java平台的一大特色,开发人员可以根据需要创建自己的类加载器,例如实现模块化、热部署或者加密解密类等高级功能。自定义ClassLoader通常需要重写findClass()或loadClass()方法,以控制类的加载...

    java classloader

    `CH_05.package与import机制.pdf`可能讲解了与ClassLoader相关的包和导入机制,因为它们与类的组织和加载密切相关。 `CH_03.Java与MS Office.pdf`、`CH_04.用Visual Studio.net来操控Java虚拟机.pdf`、`CH_06.Ant....

    理解Java ClassLoader机制

    同时,当遇到“双亲委派模型”(Parent Delegation Model)引发的问题,如类加载异常时,了解ClassLoader的运作方式可以更快定位和解决问题。 总的来说,Java ClassLoader机制是Java平台的核心特性之一,它使得程序...

    JVM ClassLoader简析

    首先,ClassLoader可以分为三种基本类型:Bootstrap ClassLoader、Extension ClassLoader和Application ClassLoader。Bootstrap ClassLoader是JVM启动时的第一个ClassLoader,负责加载JDK的`&lt;JAVA_HOME&gt;\lib`目录下...

Global site tag (gtag.js) - Google Analytics