一、基础知识
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应用,从而维持适当的可见性关系?
如果类装载器之间存在层次关系,那么这种层次关系允许扩展到什么程度?
多个EJB之间存在固有的关系,但Web应用没有。那么,EJB应用的装载方式是否应该和Web应用的不同,以便保持Web应用的完整性?
2.1 在EJB 2.0之前
在EJB 2.0 Public Final Draft 2之前,供应商在选择类装载模式方面有着很大的自由。如果JSP页面和Servlet要使用某个EJB,那么它们只需能够装入EJB的Home接口、远程接口、公共类和Stub类。这里所谓的公共类包括异常类、参数类等,它们应该放入一个依赖JAR文件,作为一个有着依赖关系的包装入。在这种模式下,供应商必须为依赖于EJB的Web应用选择一种装入Home接口、远程接口和Stub类的方式。
下面是这种类装载模式的一个简单实现(客户端应用在另一个虚拟机之内运行,与所有其他组件隔离,因此下面的模型不包含客户端应用的类装入过程。):
在这个模型中,每一个EAR应用将由一个定制的EAR类装载器实例装入,EJB应用和Web应用都由EAR类装载器的定制子类装载器装入。在EAR文件中,所有准备给一个以上应用共享的类都由EAR类装载器装入,包括所有公用依赖库和资源适配器包。如果一个类由EAR类装载器装入,它将自动地可供所有由子类装载器装入的类使用。
所有EJB应用都由单一的EJB类装载器装入,这个EJB类装载器是EAR类装载器的子类装载器。即使存在多个不同的EJB JAR文件,它们也都由同一类装载器装入。这一机制有利于同一JVM之内不同应用之间进行的EJB到EJB的调用。
每一个Web应用都在不同的类装载器中装入,保持类之间的隔离。这是因为,如果每一个Web应用有一个名为index.jsp的文件,则从该JSP页面生成的Servlet会有同样的类名字。由于每一个Web应用应该能够装入该Servlet的自己的版本,因此所有Web应用必须在各自的类装载器中隔离起来。
Web应用要使用在同一EAR文件内部署的EJB,必须能够看到这些EJB的外部接口和Stub实现类。由于EJB和Web应用的类装载器属于平等的关系(不是父-子关系),Web应用不能直接看到必需的类文件。然而,Web应用类装载器和EJB应用类装载器有着相同的父类装载器。为了让Web应用能够使用EJB的类文件,EJB类装载器获取各个EJB的公用接口和它们的Stub实现文件,然后把它们“导出”给EAR类装载器,在EAR类装载器中它们可以被EAR中的所有应用访问。公用接口和Stub实现文件就是客户程序调用EJB时要用到的类。这样,Web应用就能够装入使用所有EJB时需要用到的类。
依赖工具库可能在不同位置装入,具体由指定这些库的位置决定。如果一个Web应用在它的WEB-INF/lib目录下列出了一个依赖库,则该库只和这个Web应用有关,其他应用无需访问该库的内容,因此该库不必由EAR类装载器装入。在这种情况下,Web应用类装载器将装入该库的JAR文件。其他Web应用如果也要使用该库,则必须在它自己的WEB-INF/lib目录下提供。
如果依赖库由EJB和Web应用共享,则它必须由EAR类装载器装入。EAR类装载器将装入所有声明为EJB所依赖的库,使得依赖库中的类具有合适的可见性。这就允许EJB开发者把所有公用的异常类、Web应用可见的定制输入参数类、EJB封装到一个依赖库里面。这个库常常称作common.jar,但并非一定得如此命名。
与EJB和Web应用一起在EAR文件内封装的资源适配器包将自动由EAR类装入器装入。
2.2 在EJB 2.0之后
EJB 2.0规范引入了本地引用这一概念,它允许通过“传递引用”(而不是“传递值”)的方式访问并存的客户程序和EJB组件。本地引用这一概念使EAR类装载问题发生了值得注意的变化。
当EJB客户程序通过“传递引用”方式执行调用时,仅仅能够访问EJB的公用接口和Stub实现类是不够的。客户程序必须拥有一个EJB容器实现类的直接引用。在本地引用方式下,EJB的客户程序需要比以前更多的信息,这意味着在EJB 2.0之前使用的类装载模式已不再有效。为解决这个问题,EJB客户程序的类装载器必须是EJB类装载器的子孙,如下图所示:
在这个模型中,Web应用类装载器是EJB类装载器的子孙。这使得所有Web应用能够访问它们作为EJB客户程序所需要的文件。不过,为实现隔离,每一个Web应用仍在一个定制的类装载器中装入。从整体结构来看,新的模型更简洁、更容易理解,它不再要求EJB类装载器把任何文件导出给EAR类装载器。
J2EE规范没有明确规定Web应用的依赖库应该如何装载。如果一个工具类库通过WEB-INF/lib指定,很显然它应该保持隔离,只能由Web应用的类装载器装入它;然而,如果通过其他方式指定了一个工具类是Web应用的依赖库,它应该由Web应用的类装载器装入,还是应该导出给EAR类装载器?J2EE没有对这一点作出具体规定。
对于被指定为Web应用依赖库的工具类库,Silverstream应用服务器和J2EE参考实现在EAR类装载器级装入它们,WeLogic Server 6.0在Web应用类装载器中装入它们。但是,WebLogic Server 6.1改变了原来的办法,支持在EAR级别上装入Web应用依赖库。这种改变是有意义的,因为Web应用的隔离总是可以通过把工具类库放入WEB-INF/lib目录实现。