===================================
一、基础知识
1.1 J2EE应用的构成
1.2 封装和部署操作的任务
1.3 不能封装到EAR文件的组件
二、类装载模式
2.1 在EJB 2.0之前
2.2 在EJB 2.0之后
===================================
正文:
===================================
一、基础知识
1.1 J2EE应用的构成
J2EE应用由以下两种资源构成:
一个或者多个J2EE组件
一个J2EE应用部署描述器(Deployment Descriptor)
当多个异种的J2EE组件需要相互调用时,我们必须创建一个J2EE应用。在创建J2EE应用的过程中,有许多事情必须考虑,其中包括:
有哪些类型的组件可以封装到J2EE应用里?
创建J2EE应用的过程中,人们担负的各种职责。
当前J2EE封装技术的局限之处。
为满足J2EE组件交互的需要,不同的供应商所采取的类装载策略。
J2EE规范区分了两类资源:可以在容器之内运行的资源,可以封装到EAR文件内的资源。EAR是Enterprise Application ARchive的缩写,EAR文件用来把一个或者多个J2EE组件封装到单个模块里面,以便一起部署并装入到服务器上。
J2EE规范阐明了运行时容器和部署模块之间的区别。运行时容器是请求级的截取机制,为系统内的组件提供一种基础服务。部署模块是一种为那些最终将在运行时容器内执行的组件提供的封装结构。下图描述了J2EE容器的结构:
其中:
EJB容器
EJB容器用来容纳业务逻辑,并为业务逻辑截取请求。EJB容器支持EJB访问JMS、JAAS、JTA、JavaMail、JAXP、JDBC和连接器(Connector)。
Web容器
Web容器截取通过HTTP、FTP、SMTP或其他协议发送的请求。Web应用容器为Servlet和JSP页面提供的资源与EJB容器提供的资源相同。
应用客户端容器
客户端应用是独立的Java应用,它们在远程的独立JVM上运行(与Web容器和EJB容器所运行的JVM不同)。应用客户端容器为这些应用截取请求。
运行在应用客户端容器上的程序非常类似于带有main()方法的Java程序,不过,程序不再由JVM控制,而是由一个容器(也就是应用客户端容器)控制。
在应用客户端容器内运行的程序能够访问远程应用服务器上的JAXP、JDBC、JMS和JAAS资源。
Applet容器
Applet容器是一个特殊的容器,它为在浏览器内运行的Java程序截取请求。Applet容器不提供任何对其他资源(比如JDBC或JMS)的访问。在Applet容器内运行的Applet必须自己直接向应用服务器请求资源(而不是把请求发送给容器,然后由容器发送请求给应用服务器)。
对于Applet如何与EJB容器进行通信,EJB规范没有作出任何规定,但J2EE规范有这方面的规定。对于那些想要直接使用EJB的Applet,J2EE规范要求它们使用HTTP隧道。许多应用服务器提供某种形式的HTTP隧道,支持Applet直接使用EJB。
可以封装到J2EE EAR文件里面的组件与包含容器的组件之间不存在直接的关联。J2EE没有规定EAR文件至少必须包含哪些内容,EAR文件可以由任意数量的以下组件构成:
EJB应用JAR文件
EJB应用JAR文件包含一个或者多个EJB组件。
Web应用WAR文件
一个WAR文件包含一个Web应用。EAR文件可以包含多个Web应用,EAR文件中的每个Web应用必须有一个唯一的部署上下文。EAR文件的部署机制允许指定这类不同的上下文。
应用客户端JAR文件
应用客户端JAR文件包含一个准备在应用客户端容器内运行的独立Java应用,还包含一个专用的部署描述器,其构成方式和EJB JAR文件的构成方式相似。
应用客户端JAR文件除了包含运行独立客户程序所需要的类之外,还包含访问JDBC、JMS、JAXP、JAAS和EJB客户所需要的客户端库。
资源适配器RAR文件
资源适配器RAR文件包含了实现企业信息系统JCA(Java Connector Architecture)资源适配器所需要的Java类和本地库。
资源适配器不在容器内执行。相反,它们应该作为应用服务器和外部企业信息系统之间的桥接软件执行。
这些组件都在J2EE EAR文件之外分别地开发和打包,且分别拥有自己的部署描述器。然后,J2EE EAR文件通过定制的部署描述器,把一个或者多个这种组件装配成一个统一的包。
1.2 封装和部署操作的任务
在EJB、Web应用或其他组件的构造、部署和使用过程中,不同的人担负着不同的职责。J2EE规范为开发者在创建企业应用过程中的职责定义了范围广泛的平台角色(Platform Role)。尽管角色的数量众多,但它们不外乎是一种为了更好地规划和运行一个应用而设计的逻辑划分,单独的个人、小组或组织很可能扮演多个角色。在构造、部署使用EAR文件的过程中,常见的角色包括:
J2EE产品提供者
负责实现J2EE平台,包括在规范中定义的所有J2EE API和其他功能。比如,J2EE应用服务器的供应商。
应用组件提供者
负责提供J2EE组件,例如EJB应用或Web应用。J2EE规范中的许多角色都具有应用组件提供者的特征,比如文档编写者、JSP页面编写者、资源适配器开发者等。
应用组装者
负责把一个或者多个J2EE组件打包成EAR文件,构造出J2EE应用。应用组装者还要负责创建J2EE应用部署描述器,说明应用依赖的各种外部资源,比如类库、安全角色等。通常,应用组装者要用到J2EE产品提供者和工具提供者提供的工具。
工具提供者
提供自动化J2EE应用创建、打包、部署过程的工具,例如为EAR文件自动生成部署描述器的工具,自动创建EAR文件的工具。
部署者
负责把Web应用和EJB应用部署到服务器环境上。部署者不负责部署资源适配器包和应用客户端包,但可能要负责为这些组件进行额外的配置。这些组件虽然被打包成为J2EE EAR文件的一部分,部署企业应用时却不必考虑。它们是J2EE应用的一部分,但不必象Web应用和EJB容器那样经过一个“激活”过程。资源适配器包是置入合法JCA实现的简单库,虽然它们被打包进J2EE EAR文件,但它们不在J2EE容器环境下运行。因此,由于资源适配器包不包含J2EE容器,它们的激活不需要J2EE部署者的特别干预。应用客户端程序在J2EE容器的环境下运行,但它们不部署到应用服务器上。客户端程序独立运行,部署者不负责为这些程序配置容器环境。
系统管理员
负责为应用服务器和J2EE应用配置网络和运行环境,负责监视和维护J2EE应用的运行。
在本文的讨论过程中,我们主要的角色是应用组装者和部署者。
1.3 不能封装到EAR文件的组件
大多数基于Web的J2EE应用只由Web和EJB应用构成,EAR文件能够满足封装应用的基本需求。然而,EAR文件缺乏封装复杂J2EE应用的能力,比如,J2EE经常要用到下面这类组件,但它们无法在EAR文件中声明:
JDBC DataSource对象。
JMS ConnectionFactory和Destination对象。
JMX(Java Management Extension)的MBean。
在应用服务器之内运行的一些JMS消费者,例如作为ServerSession一部分运行的MessageConsumer。
当应用被部署或卸载时触发运行的一些类(这些类是供应商提供的私有扩展,没有在J2EE规范中定义,但供应商一般都提供它们)。
当前,这些组件必须由系统管理员通过专用管理接口手工配置和部署。随着时间的推移,这些组件的应用也将日益增加,为了支持应用的整体移植性,让EAR文件支持这些组件的封装也变得日益重要。
二、类装载模式
当一个类被引用时,Java虚拟机(JVM)必须装入被引用的类。JVM利用一个标准的类装入机制把类装入内存,这个从源文件装入Java类的机制称为类装载器。Java类可以从磁盘、网络或其他媒体装入,它们可以驻留在任何地方。多个类装载器可以按照父-子关系链接在一起,形成一种层次结构。由子类装载器装入的类能够看到(能够使用)由任意父类装载器装入的类;由父类装载器装入的类不能看到由任意子类装载器装入的类。类装载器、EAR文件与应用部署有着重要关系,因为应用服务器可能采用专用的类装载器部署应用模块。
如果在一个系统中,Web应用需要访问某个EJB,它就必须能够装入它所需要的那些类。由于这意味着不同模块之间的依赖关系,为了解决依赖问题,应用服务器必须为EAR类装载器考虑不同的结构方案。
独立的应用程序部署在它自己的类装载器中。这意味着,如果分别地部署一个Web应用和一个EJB应用,每个应用的类将分别装入各自的、级别相同的类装入器,Web应用内的类不能看到另一个类装载器装入的类。如果Web应用想要使用那些分开部署的EJB,就会出现问题。
在EAR文件出现之前,许多开发者先部署EJB,然后以Web应用WEB-INF\lib目录一部分的形式,再次封装同一EJB JAR文件。这样,同一类文件必须放入两个不同的地方,才能让应用正常地运行。显然,这是一种应当避免的情形。EAR文件的引入就是为了解决这个问题。EAR文件不仅是一种方便的封装格式,而且它还提供了一种特殊的类装载模式,允许EAR文件内的应用访问其他应用的类。
J2EE 1.3规范没有具体规定EAR类装载器应该如何运作。在决定类装入方式时,应用服务器供应商有着很大的自由。实现EAR类装载器之前,供应商必须决定:
EAR文件中所有应用的所有类由单一的类装载器装入,还是不同应用的文件由不同的类装载器装入?
在EAR文件中的不同应用之间,是否存在类装载器的父-子关系?例如,如果两个EJB应用依赖于log4j.jar,那么,是否应该由父类装载器装入log4j.jar,由子类装载器装入两个EJB应用,从而维持适当的可见性关系?
如果类装载器之间存在层次关系,那么这种层次关系允许
一、配置J2EE包
1.1 企业应用的开发过程
1.2 J2EE应用包的结构
1.3 EAR部署描述器
二、一个简单的实例
2.1 组件
2.2 装配应用
2.3 部署和运行应用
三、可选的应用部署描述器标记
===================================
正文:
===================================
一、配置J2EE包
在上一篇文章中,我们了解了J2EE应用封装和部署的一些基本知识,包括类装载器的不同角色和行为。接下来就可以配置和部署企业应用。为此,我们必须了解创建EAR文件的过程,以及应用部署描述器的内容。
1.1 企业应用的开发过程
构造一个企业应用的过程可以归纳为:
构造各个组件,包括EJB、JSP页面、Servlet和资源适配器等。
把这些组件打包成JAR文件格式的J2EE模块,同时提供J2EE模块部署描述器。J2EE模块是一种具有相同类型的一个或者多个J2EE组件的集合,也就是说,一个EJB模块可以包含一个以上的EJB,一个Web应用模块可以包含多个JSP页面和Servlet,一个资源适配器包可以包含多个资源适配器。
结合一个或者多个J2EE模块创建EAR文件,同时提供企业应用部署描述器,构造出J2EE应用。最简单的J2EE应用只包含一个J2EE模块,稍微复杂一点的J2EE应用可以由多个J2EE模块构成,更复杂的J2EE应用可以包含多个J2EE模块、模块里面包含的类所用到的依赖库。J2EE应用还可以包含描述部署过程的帮助文件和其他文档。
把J2EE应用部署到J2EE平台上。安装J2EE应用,然后把它与应用服务器提供的基础设施整合。在J2EE应用部署过程中,每一个J2EE模块按照该类模块的部署要求分别部署,每一个组件被部署到适合该组件类型的合适容器。例如,假设有一个my.ear文件,它包含my.jar和my.war。部署应用时,应用服务器的部署工具将把my.ear文件拷贝到应用服务器;接着,应用服务器的部署机制将提取出my.jar和my.war模块,按照当前平台的类装载要求分别部署这两个模块。如果这两个模块都部署成功,则整个J2EE应用部署成功。
J2EE企业应用开发和部署过程可以图示如下:
组件被封装成J2EE模块时带有一个部署描述器,J2EE模块可以通过部署工具创建。部署工具还可以用来部署和“反部署”独立的J2EE模块,用来把一个或者多个J2EE模块和另外的部署描述器封装成J2EE应用,用来把额外的内容加入J2EE应用或从J2EE应用删除某些内容,或者把整个应用部署到应用服务器上。
1.2 J2EE应用包的结构
J2EE企业应用包的结构很简单,它由一个或多个J2EE模块以及一个META-INF\目录下名为application.xml的部署描述器构成。文件用JAR格式打包,存储在扩展名为.ear的文件中。EAR文件可以包含依赖库,但这是可选的。EAR文件的一般结构是:
EJB .jar文件 Web应用.war文件 资源适配器.rar文件 应用客户端.jar文件 依赖库.jar文件 META-INF\ application.xml
下面是一个EAR文件的例子,它包含一个EJB模块、一个Web应用模块,不包含依赖库:
MyFirstEJB.jar MyFirstWeb.war META-INF\ application.xml
保存在EAR文件中的J2EE模块不一定要在根目录之下。例如,假设一个EAR文件包含一个EJB模块、一个资源适配器包,它的结构可以如下:
ejbs\ MySecondEJB.jar resources\ MyLegacyAdapter.rar META-INF\ application.xml
最后,下面是一个包含许多组件和依赖库的EAR文件:
ejbs\ MyThirdEJB.jar MyFourthEJB.jar resources\ MyLegacyAdapter.rar web\ MyWebApp1.war MyWebApp2.war lib\ Myxmlx.jar Mycommon.jar META-INF\ application.xml
EAR文件可以用部署工具创建,或者也可以改用JDK提供的jar工具创建。创建步骤为:
创建一个用来容纳EAR文件内容的临时目录。
把所有J2EE模块放入临时目录,创建META-INF\目录。
在META-INF\目录下创建application.xml部署描述器。
完成上述步骤之后,进入临时目录,运行jar工具创建EAR文件。
下面是一个运行jar工具的例子,它对前面例子进行打包:
jar cvf MyApplication.ear ejbs resources web lib META-INF
创建好EAR文件之后,我们就可以把J2EE应用部署到应用服务器上。
1.3 EAR部署描述器
理想情况下,我们用图形界面的工具编写application.xml文件。然而,有时我们必须手工构造或维护application.xml文件,因此理解application.xml文件用到的标记很重要。
application.xml部署描述器并不复杂,不需要很多标记就可以构造出一个合法的描述器。部署描述器的DTD定义的标记包括:
所有合法的J2EE应用描述器必须包含如下DOCTYPE声明:
<!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application
1.3//EN" "http://java.sun.com/dtd/application_1_3.dtd">
配置一个简单的application.xml部署描述器只需如下几步:
用标记声明一个企业应用。标记可以包含、和标记,供部署工具提供有关应用的描述信息使用。这些标记的内容和EJB、Web应用、资源适配器部署描述器内的同一标记的内容相同。
企业应用内的每一个模块必须有一个相应的标记描述模块。EJB用标记描述,Web应用用标记描述,资源适配器用标记描述,应用客户端程序用标记描述。除了标记之外,其他标记的内容都是指定EAR文件内包含J2EE模块的文件的相对URI,该URI必须相对于EAR文件的根。
如果企业应用包含一个Web应用J2EE模块,则必须提供一个标记和一个标记。标记是一个相对URI,指定EAR文件内包含J2EE模块的文件。这个URI和、、标记的URI同属一类。标记指定Web应用将在其下运行的上下文的名字。所有针对该Web应用内JSP页面和Servlet的请求都必须加上该上下文名字作为前缀。例如,如果部署Web应用时指定了:
<context-root>web1</context-root>
则所有对JSP页面和Servlet的请求必须是如下形式:
http://主机:端口/web1/......
在EAR文件内封装的每一个Web应用都要有一个唯一的值,任何两个Web应用不能有相同的值。如果EAR文件只包含一个Web应用,的值可以是空字符串。
二、一个简单的实例
在大多数使用EAR文件的情形中,企业应用包含一个EJB模块、一个Web应用模块,Web应用要用到EJB模块里面的EJB组件。下面的例子就属于这种情况,EJB和Web应用不需要任何依赖库。下面我们来看看构造这个例子的具体过程。
2.1 组件
在这个例子中,一个Servlet在无状态会话EJB的远程接口上调用invoke()方法,Servlet和EJB输出一些文字信息表明程序已经成功执行。如果控制台出现异常报告,它很可能意味着组件封装存在问题。本例的所有EJB源文件都属于test包,Servlet属于未命名的包。本例用到的Java文件包括:
MyEnterpriseServlet.java:Servlet的实现类,执行对EJB的调用
MyEnterprise.java:EJB的远程接口。
MyEnterpriseHome.java:EJB的Home接口。
MyEnterpriseBean.java::EJB的实现类。
MyEnterpriseBean.java的实现代码是:
quote:
--------------------------------------------------------------------------------
package test;
import javax.ejb.*;
public class MyEnterpriseBean implements SessionBean {
private InitialContext ctx;
public void ejbCreate() {}
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext c) {}
public void invoke() {
System.out.println("正在执行EJB.");
}
}
MyEnterpriseServlet.java Servlet的实现代码是:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import javax.naming.*;
public class MyEnterpriseServlet extends HttpServlet {
public void service(HttpServletRequest req, HttpServletResponse res)
throws IOException{
res.setContentType("text/html;charset=GB2312");
PrintWriter out = res.getWriter();
try {
System.out.println("正在服务器上运行Servlet");
InitialContext ctx = new InitialContext();
test.MyEnterpriseHome eHome = (test.MyEnterpriseHome)
ctx.lookup("MyEnterpriseEJB");
test.MyEnterprise e = eHome.create();
e.invoke();
} catch(Exception e) {
out.println("异常: " + e);
System.out.println("异常: " + e);
}
out.println("<html><head><title>测试</title></head>");
out.println("<body>");
out.println("<h1>检查控制台确信EJB已经被调用</h1>");
out.println("</body></html>");
}
}
提纲:
===================================
一、模块次序问题
二、依赖库的问题
2.1 解决方案之一
2.2 解决方案之二
2.3 依赖库应用实例
===================================
正文:
===================================
在前面两篇文章中,我们了解了J2EE应用封装和部署的基本概念和实践操作,下面我们来看看几个可能遇到的问题。
一、模块次序问题
J2EE规范没有对EAR文件内的J2EE模块应该如何部署作出任何规定。特别地,J2EE规范没有明确规定部署模块的次序。如果一个模块中的某个组件要用到另一个待部署模块的组件,它可能会带来问题。
因此,必须注意大多数应用服务器以如下步骤部署EAR文件:
EAR文件内的所有资源适配器作为基本连接器部署。如果存在多个资源适配器,则它们的部署次序就是它们在application.xml部署描述器中列出的次序。
部署所有EJB模块。由于EJB可能在初始化期间用到某些资源适配器,所以EJB的部署在资源适配器之后。如果存在多个EJB模块,它们的部署次序将是它们在application.xml中列出的次序。
部署所有Web应用模块。由于Web应用初始化期间可能用到资源适配器和EJB,所有Web应用在这两者之后部署。如果存在多个Web应用模块,它们部署次序就是它们在application.xml中列出的次序。
二、依赖库的问题
在J2EE应用封装和部署过程中,最常见的问题出现在工具类和支持类上。封装Web应用或EJB应用时,这些库应该放在哪里?Web应用和EJB应用一般都有被“卸出”(这里指装入的反向过程)的能力,这种能力由部署时装入它们的类装载器支持。如果我们把工具类和支持类放入应用服务器的标准类路径,这些类很可能完全失去被卸出的能力。这样,如果Web应用或EJB应用要更新某个库的版本,重新部署Web应用或EJB应用时,包含工具类和支持类的依赖库也要重新部署。在这种情形下,把工具类放入应用服务器标准类路径很不方便,因为每次部署Web应用和EJB应用时,都要重新启动整个应用服务器,这显然不是理想的选择。
那么,在J2EE标准定义中,假定依赖库放在哪里才能实现运行时的重新部署(热部署)呢?有两个简单的方案,但从根本上来说两者都不甚合理:
以JAR文件形式封装的依赖库可以放入Web应用的WEB-INF\lib目录。一般地,WEB-INF\lib目录基本上只用来存放Servlet,JSP页面和Servlet会在读取新类时寻找该目录。如果工具类库只供一个Web应用的JSP页面和Servlet使用,应该说这个方案已经足够。然而,如果EJB组件、JMS消费者、启动应用的类和关闭应用的类也要用到同一工具类库,这种方案不再有效,因为对于它们来说,WEB-INF\lib目录是不可见的。
除了把工具类库放入WEB-INF\lib目录之外,同时在每一个EJB JAR文件中包含一份完整的拷贝。部署EJB时,EJB类装载器将只在它自己的JAR文件中寻找被引用的工具类,不去查看其他已部署EJB应用的JAR文件和WEB-INF\lib目录。如果有几个EJB应用都要用到同一个工具类库,则在每一个JAR文件中放入该类库的一份拷贝就能解决问题。虽然这种方案实现了依赖库的热部署能力,但它明显地不够完善。封装JAR文件的目的是为了提高应用的模块化程度,把同一个类文件放入多个JAR包正好是背其道而行之。此外,多次复制同一组类无谓地加大了应用的体积。最后,即使只改变一个库,每一个JAR文件也都要重新构造,从而使构造过程复杂化。
下面我们来看两种较为有效的解决方案。
2.1 解决方案之一
要解决这个问题,一种可能的方案是在J2EE中避免使用多个JAR文件,把所有EJB和工具类聚集成单个包。EJB 2.0规范正在推动几个项目进行这方面的工作。这个规范要求参与关系的实体EJB使用本地引用,因此参与关系的各个EJB必须封装到同一个JAR文件。
由于新的规范要求避免使用远程关系,因此,许多供应商正在考虑提供能够把多个EJB JAR文件合并的工具。这些工具能够读取两个合法的EJB JAR文件,把它们的类和部署描述器合并成单个统一的包。但应当注意的是,即使所有EJB组件都聚合到了单个JAR文件里面,避免了在多个EJB之间复制依赖库,但如果Web应用也要用到依赖库,则WEB-INF\lib目录下仍旧需要依赖库的副本。
此外,对EJB应用模块化的需求仍旧存在,许多人希望能够单个地部署EJB。因为重新部署一个JAR文件时,该JAR文件内的每一个EJB都将重新部署,如果实际要部署的只是单个EJB,部署过程中就会出现许多不必要的操作。
2.2 解决方案之二
随着JDK 1.3的发布,Sun重新定义了支持可选包必不可少的“扩展机制”。这个扩展机制支持两方面功能:
JAR文件可以声明自己对其他JAR文件的依赖性。
类装载器经过了修改,能够在可选的包和应用路径中搜索类。
J2EE 1.3规范要求应用服务器必须提供这方面的支持。这就要求部署描述工具能够装载所有通过扩展机制定义的可选库。它同时也意味着,如果一个应用服务器或部署工具能够在运行时卸出或重新部署那些通过扩展机制使用库的EJB应用,那么,该应用服务器或工具也支持所有依赖库的卸出或重新部署。
这种扩展机制为Web应用WAR文件和EJB应用JAR文件指定自己需要哪些企业应用EAR文件中的依赖库提供了一种标准化的方法。那么,这种扩展机制是如何工作的呢?每一个JAR文件里都有一个manifest(意为载货单、旅客名单)文件,这个文件由jar工具自动创建,默认名字是manifest.mf。JAR文件可以在manifest文件中加入一个Class-Path属性,引用它所依赖的JAR文件。我们可以手工编辑manifest.mf文件,在原有内容的基础上,添加Class-Path属性。实际上,许多供应商提供的EJB封装工具会在封装过程中处理依赖类,自动创建合适的manifest.mf文件,使这个文件包含正确的Class-Path属性。
如果在创建EJB JAR文件时修改了manifest.mf,引入了Class-Path属性,在生成一个新的EJB应用文件时,应用服务器提供的容器生成工具必须保存这个值。在WebLogic Server 6.1下,如果EJB JAR已经在manifest.mf文件中包含Class-Path属性,weblogic.ejbc工具会在生成新EJB应用时保存这个值。当前,专门用来创建Class-Path属性并把它插入manifest.mf的工具还没有出现,所以这个工作有时还需要通过手工编辑JAR文件的manifest文件进行。
Class-Path属性的值是用来搜索工具类库的相对URL。这个URL总是相对于包含Class-Path属性的组件(而不是EAR文件的根)。单个Class-Path属性内可以指定多个URL,一个manifest文件可以包含多个Class-Path属性。Class-Path属性的一般格式是:
Class-Path: 列出用空格分隔的多个JAR文件名字
下面是一个例子:
Class-Path: mylog4j.jar xmlnew.jar foo/bar/myutil.jar
在J2SE应用中使用这种扩展机制时,Class-Path属性可以引用目录。然而,对于全部内容都包含在JAR文件中的J2EE应用,Class-Path属性只能引用其他JAR文件。此外,在manifest文件中,Class-Path属性声明必须在一个独立的行上,以便与其他属性声明区分。
这种扩展机制的功能非常强大。特别地,通过创建一个以解析次序为最终次序的、包含所有类的统一类路径,它能够方便地解决循环引用问题。例如,在下面这个例子中,假设首先被解析的是MyEJB1.jar。MyEJB1.jar引用了:
Class-Path: jaxp1.jar MyEJB2.jar ..\xmlnew.jar
此时,类装载器将解析MyEJB2.jar。MyEJB2.jar引用了:
Class-Path: jaxp1.jar MyEJB1.jar
类装载器最终使用的“应用级类路径”将是:
Class-Path: jaxp1.jar MyEJB2.jar ..\xmlnew.jar MyEJB1.jar
2.3 依赖库应用实例
下面这个例子示范了一个企业应用各种可能的类装载情形。它示范了多个EJB模块、多个Web应用,以及在这些应用之间多个共享的依赖库。通过这个例子,我们可以了解应用服务器从不同应用装载各个类的过程。当应用运行时,通过输出不同类的类装载器层次关系,我们可以了解:是否所有的类都通过单个类装载器装入,还是通过不同的类装载器装入;如果通过多个类装载器装入,这些装载器有什么关系。
这个例子包含两个EJB模块、两个Web应用模块、七个在不同情形下使用的依赖类库。EAR文件的结构如下:
MyDepend1-container.jar
MyDepend2-container.jar
MyWebApp1.war
MyWebApp2.war
TestUtil1.jar
TestUtil2.jar
TestUtil3.jar
TestUtil4.jar
TestUtil5.jar
TestUtil6.jar
TestUtil7.jar
META-INF\
application.xml
每一个“MyDepend?-container”JAR文件包含一个EJB。每一个Web应用中包含一个名为TestServlet的Servlet。每一个工具类库包含一个类,这个类有一个方法,它的功能是输出类装载器的层次关系。这个例子测试了EAR文件内许多不同的类装载情形,其中包括:
当一个EJB用到一个依赖库,且在EJB manifest里面的类路径中引用它时,它是如何装载的?
当不同EJB模块内的多个EJB共享一个依赖库,且依赖库通过各个EJB的manifest类路径指定时,它是如何装载的?
当Web应用引用一个依赖库,且通过Web应用模块的manifest路径引用依赖库时,它是如何装载的?
当Web应用引用一个依赖库,且依赖库保存在Web应用模块的WEB-INF\lib目录下时,它是如何装载的?
当一个EJB模块和一个Web应用都在它们的manifest路径中引用一个依赖库时,依赖库是如何装载的?
要执行这个例子,请先把MyDepend.ear文件(从本文后面下载源文件)部署到服务器上,然后运行各个Web应用的TestServlet。TestServlet