WebSphere的类加载机制和故障排查

在部署WebSphere应用的过程中,经常会发生诸如:ClassCastException、ClassNotFoundException、NoClassDefFoundException、UnsatisfiedLinkError的错误。这种有关“类”(Class)的错误,往往来无影——开发环境好的,怎么在生产环境就有问题;而且去无踪——单独建立一个Profile部署一下就没问题了,把Jar包换个目录就OK了。其实要解决这些怪异的问题,首先要了解WebSphere的类加载(Class loader)机制。

下文主要内容来自http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.nd.doc/info/welcome_nd.html 和IBM developworks中的《如何在WebSphere中解决jar包冲突》一文,我把个人觉得最容易理解的部分总结在一起,方便学习和快速的解决问题。

使用的类装入器以及使用顺序

WebSphere Application Server 的运行时环境按以下顺序使用下列类装入器来查找和装入应用程序的新类:

  1. Java 虚拟机创建的引导程序、扩展和 CLASSPATH 类装入器引导程序类装入器使用引导类路径(通常是 jre/lib 中的类)找到并装入类。扩展类装入器使用系统属性 java.ext.dirs(通常是jre/lib/ext)找到并装入类。CLASSPATH 类装入器使用 CLASSPATH 环境变量查找和装入类。CLASSPATH 类装入器装入 WebSphere Application Server 产品在 j2ee.jar 文件中提供的 Java 2 Platform, Enterprise Edition(J2EE)应用程序编程接口(API)。由于这个类装入器装入 J2EE API,所以,可以将依赖于 J2EE API 的库添加到类路径系统属性中以扩展服务器类路径。但是,扩展服务器的类路径的首选方法是添加共享库。
  2. WebSphere 扩展类装入器WebSphere 扩展类装入器装入在运行时需要的 WebSphere Application Server 类。扩展类装入器使用 ws.ext.dirs 系统属性来确定装入类时所使用的路径。ws.ext.dirs 类路径中的每个目录和这些目录中的每个 Java 归档(JAR)文件或 ZIP 文件都添加到此类装入器使用的类路径中。如果安装在服务器上的应用程序模块引用了与资源提供程序相关联的资源,并且该提供程序指定了资源驱动程序的目录名称,那么 WebSphere 扩展类装入器还将资源提供程序类装入到服务器中。
  3. 一个或多个应用程序模块类装入器,它们负责装入在服务器中运行的企业应用程序的元素应用程序元素可以是 Web 模块、企业 bean(EJB)模块、资源适配器归档(RAR 文件)和依赖项 JAR 文件。应用程序类装入器按照 J2EE 类装入规则从企业应用程序装入类和 JAR 文件。WebSphere Application Server 允许使共享库与应用程序相关联。
  4. 零个或多个 Web 模块类装入器缺省情况下,Web 模块类装入器装入 WEB-INF/classes 和 WEB-INF/lib 目录的内容。Web 模块类装入器是应用程序类装入器的子代。可以指定使用应用程序类装入器来装入 Web 模块的内容,而不是使用 Web 模块类装入器来装入这些内容。

关于WebSphere的类加载器的层次结构,以下的几点说明可能更有助于进一步的理解类的查找和加载过程:

  • 每个类加载器负责在自身定义的类路径上进行查找和加载类。
  • 一个子类加载器能够委托它的父类加载器查找和加载类,一个加载类的请求会从子类加载器发送到父类加载器,但是从来不会从父类加载器发送到子类加载器。
  • 一旦一个类被成功加载,JVM 会缓存这个类直至其生命周期结束,并把它和相应的类加载器关联在一起,这意味着不同的类加载器可以加载相同名字的类。
  • 如果一个加载的类依赖于另一个或一些类,那么这些被依赖的类必须存在于这个类的类加载器查找路径上,或者父类加载器查找路径上。
  • 如果一个类加载器以及它所有的父类加载器都无法找到所需的类,系统就会抛出ClassNotFoundExecption异常或者NoClassDefFoundError的错误。

类装入器隔离策略

WebSphere中对类加载器有一些相关的配置,称为类加载器策略(class loader policy)。类加载器策略指类加载器的独立策略(class loader isolation policy),通过类加载器策略设置,我们可以为WAS和应用程序的类加载器进行独立定义。

每个WAS可以配置自己的应用程序类加载器策略,WAS中的每个应用程序也可以配置自己的Web模块类加载器策略,下面我们对这两种策略分别介绍。

1.应用服务器(WAS)配置:应用程序类加载器策略

应用服务器对应用程序类加载器策略有两种配置:

  • Single:整个应用服务器上的所有应用程序使用同一个类加载器。在这种配置下,每个应用程序不再有自己的类加载器。
  • Multiple:应用服务器上的每个应用程序使用自己的类加载器。

2.应用程序配置:Web模块类加载器策略

应用程序中对Web模块类加载器有两种配置:

  • Application:整个应用程序内的所有的实用程序jar包和Web模块使用同一个类加载器。
  • Module:应用程序内的每个Web模块使用自己的类加载器。应用程序的类加载器仍然存在,负责加载应用程序中Web模块以外的其它类,包括所有的实用程序jar包。

从上面的定义可以看出,不同的类加载器策略的配置下,类加载器的层次结构上的某些类加载器可能不存在。比如在应用程序服务器的应用程序类加载器策略定义为single的情况下,应用程序的类加载器将不存在,同一个应用服务器上的所有应用程序将共用同一个类加载器,这也就意味着不同的应用程序之间的类是共享的,应用程序间不能存在同名的类。

类装入器方式

类加载器有一个重要的属性:委托模式(Delegation Mode,有时也称为加载方式:Classloader mode)。委托模式决定了类加载器在查找一个类的时候, 是先查找类加载器自身指定的类路径还是先查找父类加载器上的类路径。

类加载器的委托模式有两个取值:

  • Parent_First:在加载类的时候,在从类加载器自身的类路径上查找加载类之前,首先尝试在父类加载器的类路径上查找和加载类。
  • Parent_Last:在加载类的时候,首先尝试从自己的类路径上查找加载类,在找不到的情况下,再尝试父类加载器类路径。

有了委托模式的概念,我们可以更加灵活的配置在类加载器的层次结构中类的加载和查找方式。表1中给出了在WebSphere的类加载器层次结构中各个类加载器的委托模式的定义,并给出了不同的类加载器内类的生命周期。

问题解答

具体分析引起jar包冲突的情况,主要有三种:

  • 多个应用程序间jar包冲突:多个应用程序间由于使用了共享jar包的不同版本而造成jar包版本冲突。
  • 应用程序中多个Web模块间jar包冲突:同一个应用程序内部,不同的Web模块间同时使用一个jar包的不同版本而造成jar包版本冲突。
  • 应用程序中同一个Web模块内jar包冲突:同一个应用程序内部,同一个Web模块内,由于需要同时使用同一个jar包的两个版本而造成的jar包冲突

本部分根据这三种jar包冲突的情况,讨论三种解决jar包冲突的办法,并具体讨论三种解决办法的实现步骤和适用情况:

  • 共享库方式解决jar包冲突:主要解决应用程序间的jar包冲突问题
  • 打包到Web模块中解决jar包冲突:主要解决应用程序中多个Web模块间jar包冲突问题
  • 命令行运行方式解决jar包冲突:主要解决应用程序中同一个Web模块内jar包冲突问题

详细操作过程参考《如何在WebSphere中解决jar包冲突》一文

扩展阅读

类装入器

http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.base.doc/info/aes/ae/crun_classload.html

类装入异常

http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.nd.doc/info/ae/ae/rtrb_classload_viewer.html

类装入器查看器设置(排查重要工具)

http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.nd.doc/info/ae/ae/utrb_classload_viewer.html


你可能感兴趣的:(websphere)