JBOSS启动的问题解决到CLASSLOADER在JBOSS的载入
转载自scholer
一,问题
最近使用JBOSS做WEB开发的容器,发布本地的一个应用的时候,发现在日志中总是报错,此应用也启动不起来,具体的日志如下:
E
- RROR context.ContextLoader - Context initialization failed
- org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to import bean definitions from relative location [common/webx-root.xml]
- Offending resource: ServletContext resource [/WEB-INF/webx.xml]; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from ServletContext resource [/WEB-INF/common/webx-root.xml]; nested exception is java.lang.NoSuchMethodError: org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseBeanDefinitionAttributes(Lorg/w3c/dom/Element;Ljava/lang/String;Lorg/springframework/beans/factory/config/BeanDefinition;Lorg/springframework/beans/factory/support/AbstractBeanDefinition;)Lorg/springframework/beans/factory/support/AbstractBeanDefinition;
- Caused by:
- org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from ServletContext resource [/WEB-INF/common/webx-root.xml]; nested exception is java.lang.NoSuchMethodError: org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseBeanDefinitionAttributes(Lorg/w3c/dom/Element;Ljava/lang/String;Lorg/springframework/beans/factory/config/BeanDefinition;Lorg/springframework/beans/factory/support/AbstractBeanDefinition;)Lorg/springframework/beans/factory/support/AbstractBeanDefinition;
- Caused by:
- java.lang.NoSuchMethodError: org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseBeanDefinitionAttributes(Lorg/w3c/dom/Element;Ljava/lang/String;Lorg/springframework/beans/factory/config/BeanDefinition;Lorg/springframework/beans/factory/support/AbstractBeanDefinition;)Lorg/springframework/beans/factory/support/AbstractBeanDefinition;
一层层的看日志信息:
直接的错误信息是说不能加载相关的bean对象,然后继续看,为什么呢?说是在加载webx-root.xml的时候出错,然后继续看:
说是:
- java.lang.NoSuchMethodError: org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseBeanDefinitionAttributes(Lorg/w3c/dom/Element;Ljava/lang/String;Lorg/springframework/beans/factory/config/BeanDefinition;Lorg/springframework/beans/factory/support/AbstractBeanDefinition;)Lorg/springframework/beans/factory/support/AbstractBeanDefinition;
这段呢,说是没有这样的方法:parseBeanDefinitionAttributes,这个可是SPRING里面的啊,然后去网上搜索了一下,大致得到的结论有:
1.是配置文件错误---仔细分析日志就知道,不是这样的。
因为一直对JBOSS不是很熟悉,以前用TOMCAT做开发比较多,所以开始就没有想到部署的包有问题。因为同样的war包在别的地方是OK的。
说明还是我这里的问题。
2.继续查找,还是回头来仔细分析日志,发现最终还是spring报的错误,联想到这个具体的信息,猜测可能是spring的包出问题了。
可是应用里面的包都是OK的啊?那肯定是我环境的问题了。
原因:
然后去研究JBOSS的目录,发现JBOSS每次发布的时候都会生成一些临时文件到\server\default\tmp\deploy下面去,每次我发布的时候也都清除一下。
会不会是缓存出了问题?仔细查找这个临时目录下的文件,发现发布之后,生成了一个tmp3853123996149741254spring-2.0.7.jar的文件,而我确认我的应用需要的SPRING的版本是2.5.6,到这里终于恍然大悟:发布之后我的应用采用的是这个2.0.7的spring包,而不是我的应用WEB-INF\lib下面的。
那这个包是哪里生成的?仔细查找,发现在\server\default\deploy\jboss-spring-jdk5.deployer目录下就有一个。
而JBOSS的机制就是,发布的时候将\server\default\deploy下面的一些基础的应用一起发布,而这些基础应用里面包括上面的spring发布在temp的临时目录下,是作为所有应用的基础包,也就是说这些包的优先级要高于每个普通应用的包的。
解决的方法也很简单:
替换掉\server\default\deploy\jboss-spring-jdk5.deployer目录下的spring-2.0.7.jar为新的spring-2.5.6.jar即可。
注:我用的JBOSS版本是4.2.2GA.
二,JBOSS的加载机制
下面就来简单讲解一下JBOSS的JAR加载顺序:
1) org.jboss.Main.main(String[]) 为入口.
2) main 函数创建一个名叫”jboss”的线程组, 然后创建一个属于该组的线程, 在线程中执行boot方法.
3) boot 方法首先处理main函数中的参数(及一些其它的系统环境设置), 接着就用系统的属性创建了org.jboss.system.server.ServerLoader实例[new ServerLoader(props)].
4) ServerLoader 注册Jboss相关的类路径, 包括XML解析器, jboss-jmx.jar, concurrent.jar及其它的一些额外的类路径.
这里一般都是在JBOSS_HOME\lib下面的jar.
5) ServerLoader 通过load(ClassLoader)方法创建Jboss Server实例. 参数ClassLoader是ClassLoader parentCL = Thread.currentThread(). getContextClassLoader( )得到的当前线程的类加载器. 创建的Server实例是org.jboss.system.server.Server接口的实现. load(ClassLoader)方法的细节:
用jar包及在ServerLoader中注册的类路径创建一个URLClassLoader的实例, 把传入的ClassLoader作为该URLClassLoader的parent.
Server 接口的实现类由系统属性 jboss.server.type决定, 默认是 org.jboss.system.server.ServerImpl.
URLClassLoader 通过无参构造函数加载Server接口实现的实例. 在加载前把当前线程的类加载器置为该URLClassLoader, 在加载完成后再置回之前传入的ClassLoader.
6) Server 实例用系统属性进行初始化[server.init(props)].
7) 服务起动[server.start()]. 起动过程的默认实现如下:
把当前线程类型加载器置为加载该Server接口实现实例的ClassLoader.
在jboss域内, 通过MBeanServerFactory的createMBeanServer(String)方法创建MbeanServer实例.
在MBean Server上注册ServerImpl和ServerConfigImpl两个MBean.
初始化统一的类加载仓库(unified class loader repository), 用来装载服务器配置目录及其它可选目录下的jar文件. 对于每一个jar文件和类目录都会创建一个相应的org.jboss.jmx.loading.UnifiedClassLoader实例, 并且注册到统一的仓库中. 其中一个UnifiedClassLoader实例会被设置为当前线程上下文的ClassLoader. [?: This effectively makes allUnifiedClassLoaders available through the thread context class loader.]
接下来创建org.jboss.system.ServiceController的MBean实例. ServiceController管理JBoss MBean服务的生命周期.
需要注意的是:JBOSS在加载自带的核心JAR之后,将会优先加载下面的两个目录
1、D:\jboss\server\default\lib
2、D:\jboss\server\default\tmp\deploy
其中1会在2之前加载,而目录2是发布某个WAR,EAR,EJB等应用之后生成的临时的jar,这些应用的所有JAR会生成诸如temp*.jar的形式。本文开头提到的错误也就是在目录1中存在一个spring.jar的包,优先于各个应用的spring.jar包。