摘要
Enterprise JavaBeans™(EJB™)是Java EE™平台规范中定义的一种组件模型,用于对分布式企业应用中的业务逻辑进行封装。使用EJB技术编写的组件需要部署到EJB容器中才能运行。通过容器,应用服务器向EJB组件提供了多种底层系统服务,诸如事务控制、分布式服务、安全控制等,因此,使用EJB技术开发业务组件,可以提高应用的开发、维护的效率,同时,也将提高应用的可移植性及可靠性。应用开发人员可以更专注于特定的业务需求与模型。
金蝶Apusic应用服务器提供了对EJB3.0规范的完整支持,在其中提供了高效而可靠的EJB容器。
实际上,EJB是由Sun公司定义的一种组件模型的技术规范,EJB组件必须符合EJB组件模型规范中关于相关的接口、实现以及部署描述等等方面的规定。根据规范,EJB组件必须使用Java语言进行编写,用于表示多层分布式企业应用中的可重用业务逻辑组件,以满足应用中特定的业务需求。EJB组件必须部署到EJB容器中运行,并可获得EJB容器提供的种种服务,如持久性、安全性、事务控制、并发访问控制等等。
同样,应用服务器的提供者也按照EJB组件模型规范,提供标准EJB组件的运行时环境,即EJB容器,同时,向运行于容器中的EJB组件提供底层系统服务。这样,符合EJB组件模型规范的标准EJB组件,可以不需要进行任何改动,运行于不同的应用服务器上。
在提供了一个严格遵循规范的EJB容器的基础上,Apusic应用服务器还提供了很多覆盖EJB组件开发、调试、部署和运行方面的增值特性。如CMP Entity Bean自动升迁技术:对于一个按照EJB1.1规范编写的CMP Entity Bean,应用服务器在运行时将其自动升级到EJB2.1,使按照EJB1.1规范编写的EJB可以使用EJB2.1规范中提供的Lazy Loading和Smart Update技术以提高应用执行的效率,同时降低维护已有应用成本。向EJB组件提供优越的服务质量和其他增值特性,这也正是Apusic应用服务器的优势所在。关于Apusic应用服务器提供的增值特性,可参考???。
EJB是基于组件的分布式计算的架构,是面向事务、分布式的企业应用中的组件。
包含处理企业数据的业务逻辑;
EJB实例由容器在运行时创建及管理;
可在部署时通过编辑环境项(environment entry)定制EJB的行为;
EJB的各种服务设置信息,如事务及安全属性,从EJB的类文件中分离出来。在部署和运行时,可通过工具对EJB的服务设置信息进行管理;
EJB部署到EJB容器后,客户端才可通过EJB容器对EJB进行间接访问;
EJB可使用任何EJB规范中指定可以使用的服务;
EJB可以不经改动代码或重新编译,即可直接装配到一个新的应用中;
当EJB被部署到不同的容器或服务器时,EJB开发者定义的客户视图(Client View)不会发生改变。
EJB的客户端有以下几种类型:
运行于相同容器或其他容器中的EJB;
一般的Java类,如Java应用程序、applet、servlet;
非Java的客户环境,如非Java语言编写的CORBA客户。
一般,EJB组件由以下几个部分组成,即组件接口、Home接口、Enterprise Bean类和部署描述文件(Message-driven Bean不具有组件接口和Home接口)。下面分别描述这些组成部分。
客户端通过EJB的组件接口访问EJB对象,组件接口中定义了可被客户端访问的业务方法(Message-driven Bean不具有组件接口)。组件接口分为远程接口和本地接口。
EJB对象通过远程或本地接口,提供远程客户访问或本地客户访问的支持。
提供远程接口的EJB拥有可被远程客户访问或本地客户访问的能力。对于提供了远程接口的对象,客户可通过标准的Java RMI(Remote Method Invocation)进行远程对象调用。
提供本地接口的EJB只可被本地组件通过本地接口进行调用。所谓本地组件,即是运行于相同Java虚拟机中的本地EJB对象。本地调用通过一般的标准Java编程语言接口进行。
EJB可以同时提供本地接口和远程接口,但一般只提供二者之一。
EJB2.0规定了通过Home接口来提供客户端创建、清除和在同种类型的EJB中查找特定EJB对象的方法(Message-driven Bean不包含Home接口)。对于提供远程接口的EJB,需要提供远程Home接口;提供本地接口的EJB,需要提供本地Home接口。
Home接口由EJB开发人员编写,远程Home接口必须扩展(extend)javax.ejb.EJBHome接口;本地Home接口必须扩展(extend)javax.ejb.EJBLocalHome接口。
EJB客户端通过标准的JNDI(Java Naming and Directory Interface™)API定位Home接口。
EJB3.0中,Home接口的功能由依赖注入以及可选的生命周期回调方法实现。EJB组件不再需要提供Home接口
Enterprise Bean类包含了组件的实现细节。
Enterprise Bean类由EJB开发人员编写,EJB2.0规范中的Enterprise Bean组件,必须分别声明实现如下接口,javax.ejb.SessionBean、javax.ejb.EntityBean和javax.ejb.MessageDrivenBean。符合EJB3.0规范的Enterprise Bean组件则不须实现以上接口,而使用@Stateful、@Stateless、@MessageDriven注解标记EJB类。
Session Bean可被视为客户端程序在服务器上的部分逻辑延伸,每个Session Bean对象对应于特定的客户端,不能在多个客户端间共享。换句话说,Session Bean用于表示运行于服务器端的部分业务过程,作为客户端的代理,管理业务过程或任务,如客户对账户的借贷操作、汇率的计算,等等这些涉及逻辑、算法和工作流的种种任务。这些过程都是特定的客户行为,EJB根据这些过程在运行时创建过程实例、执行计算或者清除实例。
相对于表示业务实体的Entity Bean,Session Bean的生存时间要短,大致等于于一个客户端会话的延续时间。例如,客户通过浏览器,访问与Session Bean表示的相关业务过程,或通过一个Java应用程序或者是Applet访问的相关业务过程的持续时间,或其它Enterprise Bean访问此业务过程的持续时间。
Session Bean是非持久的,其状态不被保存到持久存储机制(如数据库、文件系统)中,尽管Session Bean本身可以执行对数据库的操作,但它并不是一种持久对象的表示。
Enterprise Bean类必须实现的接口中,定义了Enterprise Bean类的一些容器用于管理实例的回调方法。这些回调方法被容器用于与组件进行交互,当容器执行与此Bean相关的一些重要操作时,通过调用这些方法通知组件。例如,用于容器在初始化或清除组件实例时,将调用这些方法中对应的管理回调方法。由于这些方法是由容器使用的,所以,组件中不应去调用这些方法 (有状态会话bean的remove方法除外)。
Session Bean表示客户端与应用之间的会话。会话由客户端与组件之间的交互组成,一般表现为一系列的方法调用。
根据Session Bean有保持会话状态的方式,可分为有状态的和无状态的Session Bean。
无状态的Session Bean,会话状态只会在单个方法调用中被保持,一旦方法调用结束,组件将丢弃方法调用过程中保持的状态,不保持跨越方法调用的会话状态。
由于无状态Session Bean不提供跨越方法调用的状态保持,因此,对于任何客户端调用,相同类别的Session Bean实例之间没有区别。例如,某个特定客户端对某个特定的无状态Session Bean实例的方法进行了调用后,此方法调用中的状态在调用完成后即被清除,对于后续的其他客户端方法调用而言,前一个是没有影响的。因此,无状态Session Bean通常被用于一些不需要保存跨越方法调用的会话状态的计算,如汇率的换算等较为简单的操作。
由于具有这种会话状态无关的特性,无状态Session Bean通常可以在服务器启动时在实例池中创建并保持一些实例,为不同的客户端调用从实例池中分配已有的实例,免去频繁的创建、初始化和清除实例的动作。
Entity Bean用于表示保存在持久数据存储机制中的实体,为这些实体提供面向对象的视图,如关系型数据库中的业务实体数据,或传统业务系统中的业务实体。
![]() | 注意 |
---|---|
在Java EE 5规范中,EJB3.0组件模型的持久机制由独立的Java Persistence API(JPA)实现。同时,EJB3.0规定了对EJB2.1兼容,因此在Apusic服务器中,仍可按照EJB2.1的习惯使用Entity Bean。 |
Entity Bean与Session Bean的不同表现在如下几个方面:
在应用或应用服务器的运行时间外,Entity Bean状态数据仍存在于持久数据存储之中。
Entity Bean有两种持久类型:Bean管理持久类型(Bean-managed Persistence,BMP)和容器管理的持久类型(Container-managed Persistence,CMP)。对于Bean管理的持久类型,由组件模型中Enterprise Bean类的代码控制对持久存储中状态数据的访问;容器管理持久性类型的Entity Bean,则由容器生成和管理对应持久存储中状态数据的访问方法。开发者可以不用编写这些方法。
相对于Session Bean对应于特定的客户端会话而言,Entity Bean可以在客户端间被共享访问,对于多个并发的客户端访问,由容器提供了完整的事务(transaction)管理机制,保证状态数据的完整性。并且提供了通过修改部署描述,指定事务属性的能力,无需在代码中对事务边界进行控制。
容器管理持久性指由EJB容器处理Entity Bean需要的对数据库的访问。使Entity Bean可以脱离特定类型的数据库,从而具有更高的灵活性。
为使Entity Bean可以由EJB容器对持久性进行管理,需要提供Entity Bean的抽象模式(abstract schema)信息。
抽象模式作为容器管理持久性的Entity Bean部署描述的一部分,提供对Entity Bean持久域和关系的定义。之所以称其为“抽象”模式,是为了与具体的底层数据存储的物理模式区分开来。例如,在关系型数据库中,物理模式由诸如表或列等结构构成。
对于容器管理持久性的Entity Bean,需要在部署描述中定义抽象模式的名字。这些名字将可以通过使用Enterprise JavaBeas™ Query Language(EJB™ QL)编写的查询语句进行引用。例如,必须为每一个finder方法定义一个EJB QL查询语句,通过此语句定义当此finder方法被调用时,容器执行的查询。
持久域(persistent fields)
被定义的Entity Bean的持久域保存在底层的数据存储中,所有这些域构成整个Entity Bean的状态。在运行时,容器自动对这些状态与数据库中的数据进行同步。一般,容器在部署时将Entity Bean与特定的数据库表进行映射,将持久域与数据库表中的列进行映射。
关系域(relationship fields)
关系域可视为数据库表中的外键(foreign key),用于标识Bean之间的关系。
容器管理持久性的Entity Bean有以下四种容器管理关系:
one-to-one,一个Bean的单个实例关联另一个Bean的单个实例;
one-to-many,一个Bean的单个实例关联另一个Bean的多个实例;
many-to-one,一个Bean的多个实例关联另一个Bean的单个实例;
many-to-many,一个Bean的多个实例关联另一个Bean的多个实例;
容器管理关系的方向可以是双向或单向。在一个双向的关系中,涉及的Bean都有一个关系域与另外的Bean关联,通过关系域,可以从一个Bean的实例中访问关联的Bean对象,反之亦可。在一个单向的关系中,只有一个Bean拥有关联其他Bean的关系域,只能从这个Bean的实例访问被关联的Bean对象,而不可从被关联的Bean对象访问到这个实例。
EJB QL查询可以通过关系进行定位。关系的方向决定了是否可以从一个Bean定位另一个Bean。如订单作为一个Bean,订单中的项目作为一个Bean,二者拥有双向关系,即订单知道拥有哪些项目,而项目知道自己属于某个订单,因此,通过关系,可以从订单定位订单中的项目,也可以从订单中的项目定位到订单;
Entity Bean是持久业务数据的对象表示,更改Entity Bean的实例的状态,数据库中的对应信息也将自动更新。实例状态是数据库数据的视图,而不是两个分离的数据。
在企业应用中,通常出现多个客户对某些业务实体的并发访问,一般,EJB容器通过维护同一个EJB实例的多个拷贝,提供给不同的客户,以此提高对客户请求的响应速度,EJB容器提供保持实例状态的一致性的机制。而这种特性对于客户来说是透明的,因此,客户可以认为操作中的EJB实例是唯一的实例,可以不用考虑EJB实例状态在多个并发客户间的一致性。
Message-driven Bean是EJB2.0 规范中提出的的Enterprise Bean。金蝶Apusic应用服务器提供了对规范中Message-driven Bean相关内容的完整实现。Message-Driven Bean 作为EJB2.0规范中新增的一个enterprise bean 类型,除得到应用服务器管理的事务,安全和资源访问的服务之外,同时作为 JMS消息系统中的消息使用者 (Message Consumer)接收并处理应用消息。 有关JMS消息系统,请参阅JMS开发。
效率原因
在Java EE™平台中,客户端对Session Bean 和Entity Bean 的方法调用通过RMI或RMI-IIOP协议进行,这是传统的通过网络进行远程调用的方法,当调用请求通过网络传播到容器,容器则将客户端请求变成一序列的方法调用依次进行。客户端只有在容器处理完请求并返回结果后方可继续进行。
可靠性的原因
当客户端对Session Bean或Entity Bean进行调用时,必须保证服务器容器处于运行状态,如容器或网络出现错误,客户端调用将无法进行。
事件的广播
传统的RMI 或RMI-IIOP机制中,客户端在某一时刻只能与某一具体的服务器通讯, 没有任何内置的机制来将事件广播到多个服务器。
Message-driven Bean作为远程方法调用的一种替代方法,在客户端和服务器的直接方法调用之间放置了一个中间层,接收一个或多个客户端的消息,并将消息转发给一个或多个消息的使用者(Message Consumer)。
通过消息机制而非直接的方法调用,客户端可以继续执行而不必等待服务器的运行结果,服务器可以选择在方法调用完成后通知客户,而消息机制本身保证了信息传输的可靠性, 同时使用消息域(Message Domain)中的消息类型模型以达到事件广播的机制。
但是,对Message-Driven Bean 的使用也有一定的限制,如不适用于依赖于方法调用、要求具有明确的返回值才能继续的客户端程序。另外,如果在一个应用中过多的使用了Message-Driven Bean, 对应用的执行效率将会产生影响,所以不适用于对时间因素敏感的客户端程序(如在下午两点定购下午四点的机票,而在四点后才得到订购是否成功的结果,这时结果已毫无用处)。
作为一种具有JMS使用者(consumer)功能的Enterprise Bean组件模型,Message-Driven Bean由EJB容器进行管理,具有一般的JMS使用者(consumer)所不具有的优点,如对于一个Message-driven Bean,容器可创建多个实例来处理大量的并发消息,而一般的JMS使用者 (consumer)开发时则必须对此进行处理才能获得类似的功能。同时Message-Driven Bean可取得EJB所能获得的标准服务,如容器管理事务等服务。
由于与Message-driven Bean相关的主题(Topic)或队列(Queue)可以在部署时配置,因此,Message-driven Bean具有更多的灵活性。
但注意,一个Message-driven Bean在部署时只可与一个具体的主题(Topic)或队列(Queue) 建立关联。如有多个主题(Topic)或队列(Queue)需要与一个Message-driven Bean关联,则 可以在部署时部署多个Message-driven Bean类,或使用一般的JMS使用者(consumer) 。
作为Enterprise Bean组件模型之一,Message-driven Bean,具有一些与Session Bean 和Entity Bean相同的方法,但由于Message-driven Bean本身不处理客户端调用,也无会话状态,客户只能通过向与Message Driven Bean关联的队列或主题发送消息从而与Message-driven Bean 进行交互,因此,Message-driven Bean 与Session Bean 和Entity Bean之间最大的不同之处在于Message-Driven Bean不具有组件接口及Home接口。
另外,Message-driven Bean异步地处理队列(Queue)或主题(Topic)中的消息,而非方法调用。
![]() | 注意 |
---|---|
本节仅适用于Entity Bean与Session Bean,不适用于Message-driven Bean。因Message-driven Bean组件模型不同于Entity Bean与Session Bean,不具有定义客户端视图的组件接口。 |
对于Session Bean和Entity Bean,客户端只可通过在组件接口中定义的方法对Bean进行访问。组件接口定义了Bean的客户端视图。有关Bean的方法实现、部署描述设置、抽象模式以及数据库访问等等,对于客户而言是不可见的。
通过定义组件接口,可以更好地简化应用的开发和维护工作。清晰的组件接口可以将客户端与EJB层的复杂性进行分离,而且在改变Bean内部实现而不影响客户端。
组件接口被划分为本地(local)接口和远程(remote)接口,用于定义客户端的访问方式。
Enterprise Bean的远程客户端具有如下特征:
对于要访问的Enterprise Bean,远程客户可以运行在不同的计算机或Java虚拟机中,也可以运行在相同的计算机或Java虚拟机中;
可以是Web组件、Java EE应用客户端或者是另一个Enterprise Bean;
对于远程客户,访问Enterprise Bean与Enterprise Bean的位置无关。
在EJB2.1规范中,如要编写一个远程客户端可访问的Enterprise Bean,必须为组件提供一个远程接口和一个Home接口,并在远程接口中定义远程客户端可访问的业务方法。在EJB3.0规范中,可以使用@Remote注解定义远程接口。调用EJB3.0规范中的远程业务接口与客户端位置无关,无论是与会话Bean运行于不同Java虚拟机中的远程客户端,或运行于同一JVM中的本地客户端,均可通过远程接口访问会话Bean。容器为远程接口提供了位置透明性。
Enterprised Bean的本地客户端具有如下特征:
对于要访问的Enterprise Bean,本地客户必须运行在同一个Java虚拟机中;
可以是Web组件或另一个Enterprise Bean;
对本地客户而言,对Enterprise Bean的访问与Enterprise Bean所处的位置有关;
通常是某个与其他Entity Bean具有容器管理关系的Entity Bean。
在EJB2.1规范中,如要编写一个本地客户端可访问的Enterprise Bean,必须为组件提供一个本地接口和一个本地Home接口,并在本地接口中定义本地客户端可访问的业务方法。在EJB3.0规范中,可以使用@Local 注解进行标识。调用本地接口的客户端必须与会话BeanJava运行于同一Java虚拟机中。
如在容器管理关系中,某个Entity Bean可以被其他Entity Bean所关联,则它必须提供可本地访问的接口类型。Entity Bean之间关系的方向决定Entity Bean是否被其他Entity Bean所关联,例如,表示账户的Entity Bean与表示在账户上发生的历史操作之间,账户知道自己发生的所有历史纪录,可以从账户定位相关的历史纪录的集合,可以称为表示历史纪录的Bean被表示账户的Bean所关联,因此,表示账户历史纪录的Bean必须提供可本地访问的接口类型,即提供本地接口与本地Home接口。如账户与账户历史纪录之间存在双向的关系,即可从表示历史纪录的Bean定位到表示账户的Bean,则表示账户的Bean也必须提供可本地访问的接口类型。
因为本地访问的关系,参与同一个容器管理关系的Entity Bean,必须被打包到同一个EJB JAR文件中。本地访问的最大好处是效率上的提高,因为,通常本地访问要快于远程访问。
组件接口类型影响组件方法被客户端调用时的参数传递方式。
远程调用中的参数是按值传递的,传递的是对象的拷贝,但本地调用中参数的传递则是按引用传递的,与Java编程语言中的正常的方法调用相同。
远程调用中,客户端与组件相对比较独立。因为客户端与组件操作不同的参数对象拷贝,任何一方对数据的修改,不会影响另一方的数据。
在本地调用中,客户端和组件修改的是同一个对象。在编写Enterprise Bean时,应避免使用由这种传递方式带来的副作用,因为,当应用规模增长,客户端被分布到不同的物理服务器,参数的传递方式亦随之改变。
决定一个组件应该提供远程访问还是本地访问,一般有以下一些因素。
容器管理关系
如果某Entity Bean被其他Entity Bean所关联,则此Entity Bean必须提供本地访问接口。
相关Bean之间的耦合程度
耦合程度高的Bean之间存在互相依存的关系。因此,这一类Bean一般需要提供本地访问接口,以形成一个逻辑上的单元,同时提供整个单元内交互的效率。
客户端类型
如Bean将被Java EE应用客户端所访问,则应该提供远程访问接口。如果客户端是Web组件或其他Bean,提供何种类型的访问接口决定于这些组件的分布状况。
组件分布
Java EE应用可以分布在多个计算机,为提供这种分布上的可伸缩性,将被分布到不同位置的组件所访问的Bean,应该提供远程访问接口。
尽管一般Bean只提供一种类型的客户访问接口,但事实上,有一些Bean仍需要同时提供两种类型的接口。
一个合法的金蝶Apusic应用服务器的EJB,应包含如下几个部分:
部署描述:定义Bean部署信息,如包含持久类型、事务属性等信息的XML文件。通常通过使用图形化的部署工具生成。对于Apusic应用服务器,部署描述文件包括一个EJB规范中规定的ejb-jar.xml文件和一个Apusic规定的apsuic-ejb-jar.xml文件。在EJB3.0中,ejb-jar.xml中的部署描述信息可通过注解在源代码中进行标记。
组件接口:在EJB2.1提供远程客户访问能力的Bean必须提供远程接口文件和远程Home接口文件;提供本地客户访问能力的Bean必须提供本地接口文件和本地Home接口文件(不包含Message-driven Bean)。
组件类:组件接口中定义的方法,在此类文件中提供实现。
将以上文件打包成为EJB JAR文件之后,形成一个EJB模块,可连同其他模块装配到不同的表示Java EE应用的EAR文件中。
EJB组件模型的灵活性表现在:
EJB可作为表示无状态服务的对象;
EJB可作为表示无状态服务的对象,可通过向指定的消息队列或主题发送JMS消息,以实现对此对象的异步调用;
EJB可作为表示与特定客户的会话对象。此类对象在客户进行跨越方法的调用时,自动维持会话状态;
EJB可作为表示业务对象的实体对象,在多个客户间共享;
EJB可作为一个细粒度的持久对象,包含在一个粗粒度业务对象的持久状态中。
通常,被远程访问的组件往往是粗粒度的业务对象,如订单、雇员纪录;细粒度的业务对象往往不会采用可远程访问的EJB组件模型,如订单中的采购项、雇员纪录中的地址,而是采用可本地访问的EJB组件模型或是作为EJB的附属类出现。
下面是在使用EJB组件模型构建企业应用时,一些判断EJB组件模型是否适用的标准。
EJB组件是构建分布式企业应用的组件模型技术;
EJB组件规范是针对分布式企业应用制定的,是基于分布式对象技术的Java组件;EJB组件不涉及表示层的内容,因此,必须与其他表示层技术一起使用;应用服务器提供了可以解决安全性、资源共享、持续运行、并行处理、事务完整性等复杂问题的服务,从而简化了商业应用系统。
应用客户端类型的考虑;
一般,企业应用开发都会有多种类型的客户端的需求,访问相同的数据或业务逻辑。如使用Web客户提供对应用的基于Internet的访问,使用应用客户端提应用基于Intranet的访问。EJB组件模型将业务逻辑与数据封装到EJB组件中,提供对多种客户端的支持。
应用数据与业务逻辑的并发访问控制的考虑;
企业应用通常需要提供数据或业务逻辑的并发访问能力,以此保证数据的完整性,由于EJB 组件控制对后台数据的访问,并管理当前事务和数据库的内部锁定。节省了编写数据库控制逻辑的工作量,同时也保证了数据的一致性与正确性,从而降低了总编程量。
全局事务控制的考虑;
企业应用通常需要对不同的资源进行事务性的操作,如某个操作需要对数据库进行访问,同时可能需要通过JMS消息服务发送消息,或者,需要访问两个位于不同物理位置的异种数据库,这些操作必须在相同的事务环境中完成。
Apusic应用服务器提供了全局事务控制,使得对数据库的操作和对消息服务的操作可以加入到一个事务环境中,而这个特性对开发者而言是透明的,由应用服务器内部的事务管理器对事务环境中的操作进行管理。
基于访问控制的考虑;
企业应用中往往需要对某些资源进行访问控制,如需要针对不同用户对组件方法调用设置访问控制策略,对访问特定Web资源的用户设置访问控制策略等,Apusic应用服务器提供了基于用户和组的身份鉴定和授权机制,可以通过部署描述和图形工具实现对Web资源的访问控制和对EJB组件的方法级别的访问控制。
基于伸缩性的考虑;
企业应用的用户规模往往成为影响企业应用开发难度的主要因素,随着企业经营规模的扩大,用户规模也会随之增长,Apusic应用服务器提供了负载均衡与实效恢复的能力,可以在降低开发难度的情况下,通过增加服务器节点即可适应应用规模的快速增长。
基于重用性的考虑;
企业应用往往涉及很多重复的业务逻辑和数据模型,使用EJB组件模型,可以通过修改部署描述文件在不同的应用中方便地重用这些组件。