websphere解决jar包冲突.docx
《websphere解决jar包冲突.docx》由会员分享,可在线阅读,更多相关《websphere解决jar包冲突.docx(20页珍藏版)》请在冰豆网上搜索。
websphere解决jar包冲突
Jar包冲突问题是在大型Java软件开发中常常碰到的问题,系统开发人员常常会为解决类似的问题花费大量的时刻进行调试和测试,本文依照各类际情形,结合WebSphere中类加载器,讨论了几种解决jar包冲突问题的方法,并给出了具体实现的步骤及源代码。
读者定位为具有Java和WebSphere开发体会的开发人员。
读者能够学习到在WebSphere中类加载器的概念和解决jar包冲突问题的几种方法,并能够直接利用文章中提供的Java代码,从而节省他们的开发和调试时刻,提高效率。
大型的基于WebSphere的项目开发中,同一个WebSphereApplicationServer(以下简称WAS)上会部署多个应用程序,而这多个应用程序必然会共用一些jar包,包括第三方提供的工具和项目内部的公共jar等。
把这些共用的jar包提掏出来在多个应用程序之间共享,不仅能够统一对这些jar包进行保护,同时也提高了WAS的性能。
可是随着应用的不断扩大,新的应用程序的不断增加,新的应用程序会希望利用一些更高版本的共享jar包,而由于系统运行保护的需要,老的应用程序仍然希望用老版本的共享jar包,如此就必然造成了共享jar包的版本冲突。
jar包版本冲突问题是在大型应用项目的开发中常常碰到的问题,本文试图从WebSphere的类加载器入手,讨论几种在不同情形下解决jar包冲突问题的方法。
WebSphere中类加载器介绍
Jar包冲突事实上是应用程序运行时不能找到真正所需要的类,而阻碍类的查找和加载的是JVM和WebSphere中的类加载器(classloader),为此,咱们第一介绍一下WebSphere中的类加载器和一些相关的概念。
WebSphere中类加载器层次结构
Java应用程序运行时,在class执行和被访问之前,它必需通过类加载器加载使之有效,类加载器是JVM代码的一部份,负责在JVM虚拟机中查找和加载所有的Java类和本地的lib库。
类加载器的不同配置阻碍到应用程序部署到应用程序效劳器上运行时的行为。
JVM和WebSphere应用程序效劳器提供了多种不同的类加载器配置,形成一个具有父子关系的分层结构。
WebSphere中类加载器的层次结构图1所示:
图1:
WebSphere中类加载器的层次结构
如上图所示,WebSphere中类加载器被组织成一个自上而下的层次结构,最上层是系统的运行环境JVM,最基层是具体的应用程序,上基层之间形成父子关系。
JVMClassloader:
位于整个层次结构的最上层,它是整个类加载器层次结构的根,因此它没有父类加载器。
那个类加载器负责加载JVM类,JVM扩展类,和概念在classpath环境变量上的所有的Java类。
WebSphereExtensionsClassloader:
WebSphere扩展类加载器,它将加载WebSphere的一些runtime类,资源适配器类等。
WebSpherelib/appClassloader:
WebSphere效劳器类加载器,它将加载WebSphere安装目录下$(WAS_HOME)/lib/app途径上的类。
在WASv4版本中,WAS利用那个途径在所有的应用程序之间共享jar包。
从WASv5开始,共享库功能提供了一种更好的方式,因此,那个类加载器要紧用于一些原有的系统的兼容。
WebSphere"server"Classloader:
WebSphere应用效劳器类加载器。
它概念在那个效劳器上的所有的应用程序之间共享的类。
WASv5中有了共享库的概念以后,能够为应用效劳器概念多个与共享库相关联的类加载器,他们依照概念的前后顺序形成父子关系。
ApplicationModuleClassLoader:
应用程序类加载器,位于层次结构的最后一层,用于加载J2EE应用程序。
依照应用程序的类加载策略的不同,还能够为Web模块概念自己的类加载器。
关于WebSphere的类加载器的层次结构,以下的几点说明可能更有助于进一步的明白得类的查找和加载进程:
每一个类加载器负责在自身概念的类途径上进行查找和加载类。
一个子类加载器能够委托它的父类加载器查找和加载类,一个加载类的请求会从子类加载器发送到父类加载器,可是从来可不能从父类加载器发送到子类加载器。
一旦一个类被成功加载,JVM会缓存那个类直至其生命周期终止,并把它和相应的类加载器关联在一路,这意味着不同的类加载器能够加载相同名字的类。
若是一个加载的类依托于另一个或一些类,那么这些被依托的类必需存在于那个类的类加载器查找途径上,或父类加载器查找途径上。
若是一个类加载器和它所有的父类加载器都无法找到所需的类,系统就会抛出ClassNotFoundExecption异样或NoClassDefFoundError的错误。
类加载器的委托模式
类加载器有一个重要的属性:
委托模式(DelegationMode,有时也称为加载方式:
Classloadermode)。
委托模式决定了类加载器在查找一个类的时候,是先查找类加载器自身指定的类途径仍是先查找父类加载器上的类途径。
类加载器的委托模式有两个取值:
Parent_First:
在加载类的时候,在从类加载器自身的类途径上查找加载类之前,第一尝试在父类加载器的类途径上查找和加载类。
Parent_Last:
在加载类的时候,第一尝试从自己的类途径上查找加载类,在找不到的情形下,再尝试父类加载器类途径。
有了委托模式的概念,咱们能够加倍灵活的配置在类加载器的层次结构中类的加载和查找方式。
表1中给出了在WebSphere的类加载器层次结构中各个类加载器的委托模式的概念,并给出了不同的类加载器内类的生命周期。
注意:
在上表中,"JVMClassloader"因为在类加载器的最顶层,它没有父类加载器,因此其委托模式为N/A,"WebSphereExtensionsClassloader"和"WebSpherelib/appClassloader"的委托模式固定为表中的取值,不可配置,其它的类加载器的委托模式都是能够配置的。
WebSphere中的类加载器策略
WebSphere中对类加载器有一些相关的配置,称为类加载器策略(classloaderpolicy)。
类加载器策略指类加载器的独立策略(classloaderisolationpolicy),通过类加载器策略设置,咱们能够为WAS和应用程序的类加载器进行独立概念。
每一个WAS能够配置自己的应用程序类加载器策略,WAS中的每一个应用程序也能够配置自己的Web模块类加载器策略,下面咱们对这两种策略别离介绍。
1.应用效劳器(WAS)配置:
应用程序类加载器策略
应用效劳器对应用程序类加载器策略有两种配置:
Single:
整个应用效劳器上的所有应用程序利用同一个类加载器。
在这种配置下,每一个应用程序再也不有自己的类加载器。
Multiple:
应用效劳器上的每一个应用程序利用自己的类加载器。
2.应用程序配置:
Web模块类加载器策略
应用程序中对Web模块类加载器有两种配置:
Application:
整个应用程序内的所有的有效程序jar包和Web模块利用同一个类加载器。
Module:
应用程序内的每一个Web模块利用自己的类加载器。
应用程序的类加载器仍然存在,负责加载应用程序中Web模块之外的其它类,包括所有的有效程序jar包。
从上面的概念能够看出,不同的类加载器策略的配置下,类加载器的层次结构上的某些类加载器可能不存在。
比如在应用程序效劳器的应用程序类加载器策略概念为single的情形下,应用程序的类加载器将不存在,同一个应用效劳器上的所有应用程序将共用同一个类加载器,这也就意味着不同的应用程序之间的类是共享的,应用程序间不能存在同名的类。
在WebSphere中解决jar包冲突
Jar包冲突问题事实上确实是应用程序希望用某一个确信版本的jar包中的类,可是类加载器却找到并加载了另外一个版本的jar包中的类。
在上一部份介绍了WebSphere中类加载器的大体概念和相关配置以后,咱们来看如安在WebSphere中解决jar包冲突。
在WASv5版本之前,利用共享jar包的方式是将jar包放在$(WAS_HOME)/lib/app途径下,从上一部份中,咱们能够看到,那个途径正是"WebSpherelib/appClassloader"类加载器的类查找途径,WebSphere会查找那个途径以取得相应得jar包中的Java类,从而做到在WebSphereND上的多个应用程序之间共享jar包的目的。
可是如此做的一个缺点确实是这些共享jar包暴露给WebSphereND上所有的应用程序,关于那些希望利用jar包其它版本的应用程序,这些jar包也一样存在在了它们的类加载器类途径上,因此,就不可幸免的会造成版本的冲突。
在WASv5版本及以后,增加了共享库(sharedlibrary)的概念,推荐的在多个应用程序间共享jar包并幸免jar包冲突的方式是利用共享库。
具体分析引发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包冲突问题
共享库方式解决jar包冲突
在WASv5中,提供了一种专门好的机制,使得jar包只存在于需要那个jar包的应用程序的类加载器的途径上,而其它的应用程序不受它的任何阻碍,这确实是共享库(Sharedlibrary)。
共享库能够用在应用效劳器级别和应用程序级别,利用应用程序级别的共享库,其益处确实是在不同的应用程序之间利用共享jar包的不同版本。
咱们能够为一些通用jar包的每一个不同版本概念成不同的共享库,应用程序希望利用哪个版本,就把那个版本的共享库放到应用程序的类加载器的类途径上,这种方式有效的解决了应用程序之间jar包冲突的问题。
下面举例介绍概念和利用共享库的具体方式,本例中,假设存在xerces.jar包版本冲突。
1.概念共享库
系统治理员能够在WebSphere的Adminconsole中概念共享库,能够别离在Cell、Node和server的级别上概念。
步骤一:
进入Adminconsole,选择Environment>SharedLibrary>new。
如图2所示:
图2:
WebSphereAdminConsole中进入共享库页面
步骤二:
给出共享库的名字,并指定共享的文件和目录。
多个不同的文件/目录之间通过"Enter"键分隔,且不能有途径分隔符,如":
"和";"等。
如图3所示:
图3:
WebSphereAdminConsole中添加共享库
步骤三:
点击Apply或OK以后,就添加了一个名字为XercesV2.0的共享库。
记住添加完成后必然要在adminconsole保留配置。
如图4所示:
图4:
WebSphereAdminConsole中共享库列表
2.安装应用程序
进入Adminconsole,选择Applications>InstallNewApplication安装应用程序。
请参照IBMWebSphere的Adminconsole利用手册进行安装新的应用程序,此处再也不详细介绍。
3.将共享库关联到应用程序
步骤一:
进入Adminconsole,选择Applications>Enterpriseapplications,并选择需要利用共享库的应用程序。
注意:
因为要改变应用程序的设置,因此若是应用程序已经运行,需要先停掉应用程序。
如图5所示:
图5:
WebSphereAdminConsole当选择需要配置的应用程序
步骤二:
点击应用程序,进入后,选择Libraries。
如图6所示:
图6:
WebSphereAdminConsole当选择应用程序库属性
步骤三:
点击Add,为应用程序添加共享库。
如图7所示:
图7:
WebSphereAdminConsole中应用程序添加库
步骤四:
从下拉列表当选择所需要的共享库,点击OK。
如图8所示:
图8:
WebSphereAdminConsole中应用程序添加库页面指定所用的共享库
如此,XercesV2.0共享库概念的xerces版本就存在于了应用程序类加载器的类加载途径上。
注意,在添加完成后要保留效劳器的设置。
4.设置应用程序的类加载器的委托模式为Parent_Last
为了进一步避免共享库概念的jar包的其它版本已经存在于JVM或WebSphere的类加载器途径上,还需要设置应用程序的类加载器的委托方式为Parent_Last。
步骤一:
进入Adminconsole,选择Applications>Enterpriseapplications>,选择需要配置的应用程序。
如图9所示:
图9:
WebSphereAdminConsole当选择需要配置的应用程序
步骤二:
点击进入应用程序,设置类加载器的委托模式为Parent_Last。
注意:
在配置完成后,要保留配置,最后启动应用程序。
如图10所示:
图10:
WebSphereAdminConsole中为应用程序设置类加载器委托模式
通过上面的配置,即便xerces的其它版本已经存在于系统中,应用程序在运行时,其类加载器也会第一查找并加载指定的共享库中的xerces版本。
如此咱们就通过利用共享库的方式,解决了jar包版本冲突问题。
打包到Web模块中解决jar包冲突
共享库的方式,只是在应用程序的层次上,在多个应用程序之间解决了共享jar包造成的版本冲突问题,若是一个应用程序的内部,其中一个Web模块使用了一个jar包的A版本,而另一个Web模块利用那个jar包的B版本,在这种情形下造成的jar包冲突,共享库的方式是无法解决的,咱们能够考虑将其中一个在多个应用程序间共享的jar包版本,比如A版本,概念成共享库,或放在"WebSpherelib/appClassloader"类加载器途径上供多个应用程序利用,而将B版本的jar包打包到利用它的Web模块中的方式来解决冲突。
第二,目前很多在线的系统是WASv4的遗留系统,其上运行的应用程序已经利用了"WebSpherelib/appClassloader"类加载器,将jar包放在$(WAS_HOME)/lib/app目录下进行共享。
若是由于其中某个应用程序的升级或新增加某个应用程序,需要利用某个共享jar包的其它版本,在这种情形下,为了减少对系统的阻碍,也能够考虑将那个共享jar包的新版本打包到升级(或新增)的应用程序中的方式来解决jar包冲突。
由于Web模块的WebContent/WEB-INFO/lib目录在应用程序Web模块的类加载器查找途径上,因此,咱们能够把jar包放在那个目录下,Web模块的类加载器将自动查找并加载那个jar包中的类。
步骤一:
在WSADIE集成开发环境中,将冲突jar包放在Web模块的WebContent/WEB-INFO/lib目录下。
如图11所示:
图11:
WSADIE中为Web模块添加库
步骤二:
在Adminconsole中,将应用程序部署到WebSphereserver上以后,进入Applications>EnterpriseApplications,选择相应的应用程序,并确认应用程序不在运行状态(参见前面章节当选择应用程序的步骤)。
点击进入应用程序,确认应用程序的类加载器的委托模式为Parent_First,应用程序的类加载器策略为Module。
如图12所示:
图12:
WebSphereAdminConsole中应用程序属性配置页面
步骤三:
在同一个页面上,选择WebModules,点击进入。
如图13所示:
图13:
WebSphereAdminConsole当选择应用程序Web模块属性
步骤四:
点击相应的包括冲突jar包的Web模块,设置Web模块的类加载器的委托模式为Parent_Last。
注意:
在设置完成后要保留效劳器配置,并启动应用程序。
如图14所示:
图14:
WebSphereAdminConsole中为Web模块指定类加载器委托模式
将冲突jar包打包到Web模块中,并设置相应Web模块的类加载器的委托模式为Parent_Last,应用程序在运行进程中加载类的时候,那个Web模块的类加载器会第一查找WebContent/WEB-INFO/lib目录下的jar包进行类的加载;而关于其它的Web模块,由于其类加载器的委托模式仍然为缺省的Parent_First,它们的类加载器仍然第一从应用程序的共享库或WebSphere的共享途径上加载jar包中的类,从而解决了jar包冲突的问题。
命令行运行方式解决jar包冲突
不论是设置共享库,仍是将冲突jar包打包到应用程序中,其解决的问题都是在应用程序的一个Web模块中只利用了冲突jar包的一个版本的情形。
我们在开发中曾经碰到过如此的情形:
应用程序的Web模块中已经利用了1.4版本的xerces.jar,由于Web功能的扩展,在那个模块中又引入一个新的第三方工具,而那个第三方工具需要利用2.0版本的xerces.jar才能正常工作,这种情形下的jar包冲突如何解决呢?
在前面类加载器的部份已经介绍过,每一个应用程序的一个Web模块最多只能有一个类加载器,而Web模块的类加载器中加载的类的生命周期为整个应用程序的运行期,也确实是说,Web模块加载器不可能同时加载一个类的两个版本,同时,Web模块的类加载器的委托模式也是在应用程序运行前设置的,在应用程序运行期内无法改变的,因此,上面描述的在一个Web模块中同时利用两个版本的jar包的问题,象前两种方式那样配置运行在一个JVM内的类加载器的设置的方式是无法解决的。
唯一的解决方法确实是在应用程序运行的JVM外,启动另外一个JVM来运行挪用冲突jar包的代码,因为两个不同的JVM能够加载各自的类,从而解决jar包冲突问题。
这种情形下,原先利用jar包的老版本的方式(包括jar包放置途径,共享库设置方式,类加载器的委托模式等)不变,将对jar包新版本的挪用通过命令行运行方式实现。
具体做法是:
将对jar包新版本内功能的挪用,封装到一个能够单独运行的类中,在Web模块中以命令行方式运行那个类。
同时把那个类和jar包的新版本放在任意一个was可访问的途径上(比如/usr/WebSphere),在命令行的classpath参数中包括那个途径(比如/usr/WebSphere)。
下面通过举例说明命令行运行方式的编程进程,在本例中,假设TestEar应用程序的Web模块TestWar中,已经利用了conflict_v1.jar,由于新添功能需要利用conflict_v2.jar中的exampleCall()功能。
冲突jar包conflict_v2.jar提供的功能:
Packagecom.ibm.conflict;
PublicclassConflictClass{
…….
PublicstaticStringexampleCall(stringparam){
Stringrs;
……;
Returnrs;
}
……
}
不存在冲突问题时的编码举例:
若是没有jar包冲突问题,那么对那个功能的挪用是简单的,只需要将conflict_v2.jar放在应用程序自身或其父类加载器的查找途径上,然后在Web模块中直接挪用即可,如下:
代码2:
不存在冲突时的挪用方式
PublicStringmethodA(Stringparam){
……
Stringrs=ConflictClass.exampleCall(param);
……
Returnrs;
}
存在冲突后的命令行运行方式编码举例
针对jar包冲突问题,咱们需要在Web模块中做如下的修改:
步骤一:
将冲突jar包放在was可访问的途径上,比如/usr/WebSphere/conflict_v2.jar。
步骤二:
将对包括冲突代码的挪用封装到一组可单独运行的类中,它们将挪用冲突jar包的功能,并将结果以系统输出的方式打印到系统标准输出上。
将这些类封装到一个单独的jar文件中,比如workAroundConflict.jar,并将其放在was可访问的途径上,比如/usr/WebSphere/workAroundConflict.jar。
Packagecom.ibm.test;
Importcom.ibm.ConflictClass;
PublicclassWorkAround{
Publicstaticvoidmain(String[]args){
Stringparam1=args[0];
StringreturnStr=ConflictClass.exampleCall();
System.out.println(""+returnStr+"");
Return;
}
}
代码3:
将对冲突代码的挪用写入一个单独的类WorkAround
步骤三:
在Web模块中通过命令行方式挪用封装的类,通过classpath指定所有依托的jar包和类途径。
运行封装类,从系统标准输出中取得运行结果。
PublicstaticStringmethodA(Stringparam){
……
StringrtStr="";
StringlStr="";
StringrStr="";
//putallthedependencyjarhere
StringclassPath="/usr/WebSphere/conflict_v2.jar;/usr/WebSphere/workaroundConflict.jar;……";
StringclassName="com.ibm.test.WorkAround";
StringcmdLine="java-classpath"+classPath+""+className+""+param;
Try{
Processprocess=Runtime.getRuntime().exec(cmdLine,null);
process.waitFor();
Buffere