站内搜索: 请输入搜索关键词
当前页面: 在线文档首页 > J2EE Tutorial 中文版

第3章 企业Bean - J2EE Tutorial 中文版


第3章 企业Bean

Dale Green

翻译 IceShape Zeng

企业Bean是实现EJB技术的J2EE组件。企业Bean在俄EJB容器中运行,运行时环境由服务器建立(图1-5)。虽然EJB容器对开发这是透明的,但它为运行其中的企业Bean提供项事务处理等的系统级服务。这些服务是你可以快速的建立和部署企业Bean,而这些企业Bean正是构成和新业务处理的J2EE应用。

本章内容:

1、企业Bean概述

企业Bean的优点

何时需要使用企业Bean

企业Bean的分类

2、会话Bean

状态管理模式

何时需要会话Bean

3、EntityBean

EntityBean的构造

和会话Bean的不同之处

容器管理的持久性

何时需要EntityBean

4、Message-DrivenBean

Message-DrivenBean的构造

和会话Bean、EntityBean的不同之处

何时需要Message-DrivenBean

5、定义客户端访问接口

远程访问

本地访问

本地接口和容器管理的关系

选择接口类型

访问的性能

访问的参数

6、企业Bean的内容

7、企业Bean的命名约定

8、企业Bean的生存周期

有状态会话Bean的生存周期

无状态会话Bean的生存周期

EntityBean的生存周期

Message-DrivenBean的生存周期

 

一、企业Bean概述

用Java语言编写,企业Bean就是一个应用中封装了商务逻辑的服务器端组件。这些商务逻辑是实现应用程序目标的代码。例如在一个存货控制的应用程序里,企业Bean也许在checkInventoryLevel和orderProduct方法中实现了商务逻辑,通过调用这两个方法,远程客户端就可以访问应用程序提供的存货管理的服务。

企业Bean的优点

由于以下的原因,企业Bean大大简化了分布是应用的开发。首先EJB容器给企业Bean提供了系统级服务,使Bean开发者可以专注于商务问题的解决。是EJB容器而不是开发者负责项事务处理和安全授权等系统级服务的管理。其次因为企业Bean而不是客户端实现商务逻辑,客户端开发者就可以致力于客户端表述的开发,而不必为实现商务规则或者数据库访问的日常处理而编码了。结果使客户端“瘦”了许多,很明显,这个有点对于在小设备上运行的客户端来说是很重要的。最后,因为企业Bean是可移植的,应用程序组装者可以用现有的企业Bean建立新的应用程序。这些应用程序可以在任何兼容的J2EE服务器上运行。

何时需要使用企业Bean

如果你的应用程序符合以下的任一条件,你就应该考虑使用企业Bean: 

·你的应用程序需要不断的升级。为了适应不断增长的用户,你可能需要将你的应用程序组件分布在多台不同的机器上运行。虽然并不仅仅是企业Bean可以在不同的机器上运行,但企业Bean的运行位置对于客户端始终是透明的。

·需要用事务机制来保证数据完整性。企业Bean支持事务机制以提供对共享资源并发访问的管理。

·应用程序需要支持众多不同类型的客户端。只需要极少的几行代码,远程客户端就可以很容易的访问到企业Bean。这些客户都可以很“瘦”并且在理论上可以是任意数量不同类型的客户端。

企业Bean的分类

表3-1列出了三种不同类型的企业Bean。接下来的几节将详细介绍每一种企业Bean.

企业Bean类型

作用

会话Bean

完成客户端请求的动作

EntityBean

描述持久存储的商业实体对象

MessageDrivenBean

作为JMS(Java Message Service Java消息服务) API的监听者异步处理监听到的的消息

二、会话Bean

会话Bean表现连接到J2EE服务器的一个单独的客户端。客户端通过调用会话Bean的方法来访问部署在服务器上的应用程序。会话Bean完成客户端的请求,从而对客户端隐藏了服务器内复杂商务逻辑的执行过程。

正如会话Bean的名称所表示的,它代表一次会话。单独的会话Bean对象是不可以共享的,它只能有一个客户端访问,同样一次会话也只能有一个用户。和一次会话一样,会话Bean对象不是持久性对象。(就是说它的数据并不保存到数据库。)当客户端结束运行,对应的会话Bean也跟着结束并断开与客户端的联系(不保留特定客户端的任何信息)。

第四章将会介绍会话Bean的编码实例。

状态管理模式

根据状态管理模式的不同会话Bean可以分为两类:有状态(stateful)会话Bean和无状态(stateless)会话Bean。

有状态会话Bean

一个对象的状态由它的成员变量(数据成员)的状态决定。有状态会话Bean的成员变量描述一个唯一的客户端-会话Bean的关联状态。因为客户端要与对应的会话Bean进行对话,所以这种状态通常被叫做会话状态。

会话状态在整个会话期间被保留。如果客户端运行结束或者删除对应的会话Bean,这个会话就结束同时状态被清除。这种状态的短暂性并不是问题,相反,如果客户端和会话Bean的对话结束就不必要在保留会话的状态了。

无状态会话Bean

无状态会话Bean并不为客户端保留会话状态。在客户端掉用无状态会话Bean的方法时,对应会话Bean的数据成员会描述这个调用状态,但仅仅只在该方法调用期间保持这个状态。当方法调用结束,状态就被清除。除了在方法调用期间,所有同一个无状态会话Bean实例是等价的,可以被容器分配给任一客户端。

因为无状态会话Bean可以同时支持多个客户端,所以能更好的支持应用程序的可数的大量客户端。很明显,对支持相同数量的客户端的应用程序,需要的无状态会话Bean会比有状态会话Bean要少。

有时,EJB容器会在没有请求的时候把有状态会话Bean保存在内存(二级存储器Second Storage)中。不管什么时候,没有请求时无状态会话Bean都不会被保存中。所以,无状态会话Bean会比有状态会话Bean有更高的性能。

何时需要会话Bean

通常,在出现以下几种情况时你需要用会话Bean:

.在任何给定时间,只有一个客户端访问这个Bean的实例。

. Bean的状态并不需要持久保存,只在一个时间段(可能是几小时)内保持。

在以下情况下,建议采用有状态会话Bean:

. Bean需要描述一个于特定客户端的会话状态

. Bean需要在客户端的多个方法调用之间保存调用信息

. Bean作为应用程序的其他组件和客户端的中介者,呈现一个简单化的视图给客户端

.在调用接口里,Bean管理很多企业Bean的工作流(如18章的AccountControllerEJB的会话Bean例子)。

如果你的应用符合以下特性,为了得到更高的性能你应该选择无状态会话Bean:

.Bean的状态不包含客户端相关的数据

.在一个单一方法调用中,Bean已经可以为客户端完成所需要的工作。例如你可以用无状态会话Bean发一封邮件确认网络订单。

.Bean需要从数据库获取一些客户端经常访问的只读数据。你可以用这样的Bean来访问数据表中代表这个月已经卖出的产品的行。

三、EntityBean

一个EntityBean描述一个持久存储备的商业对象。商业对象的例子如:消费者,订单和产品等。在J2EE SDK中持久存储设备是一个关系型数据库。最典型的情况是一个EntityBean又一个在地层数据库中有一个表相对应,而EntityBean的每一个实例对应表中的一行数据。在第5和第6章有示例代码。

EntityBean和会话Bean的不同之处

EntityBean和会话Bean有很多不同之处。EntityBean是持久性的,允许共享访问,拥有主键并且会参与和其他EntityBean的关联。

持久性

因为EntityBean的状态保存在存储设备中,所以它具有持久性。持久性是指EntityBean的状态跨越应用程序和J2EE服务器处理过程的生存期,就是说应用程序结束或者服务器终止EntityBean的状态仍然保留。如果你是用数据库,你就是在使用持久性数据。数据库中的数据是持久性的,应为就算你关闭数据库服务器或者相应的应用程序,他们仍然是存在的。

EntityBean有两种持久性管理机制:BMP(bean-managed persistence Bean管理的持久性)和CMP(container-managed persistence 容器管理的持久性)。对于BMP,必须在EntityBean中手工编写访问数据库的代码。对于CMP,容器会自动生成访问数据库的代码,为开发者节省了为数据库访问编码。详细信息在下节容器管理的持久性中介绍。

共享访问

EntityBean可以被多客户端所共享。由于多个客户端可能同时去修改同一数据,所以在调用过程中事务机制非常重要。典型情况下EJB容器都支持事务机制。在这种情况下,可以在Bean的部署描述符中确定它的事务属性。开发者不必为事务界限编码——容器会自动划分事务界限。14章将详细描述Bean的事务机制。

主键

每一个EntityBean实例都有一个唯一对象标识。例如一个特定的EntityBean实例可能用一个特定的数字来标识。这个唯一标识就是主键,可以让客户端找到对应的EntityBean实例。更多信息请查看主键和Bean管理的持久性一节。

关系

象关系数据库中的一个表一样,EntityBean之间也会有关系。例如在一个学校登记系统中,表示学生的StudentEJB和表示课程的CourseEJB因为学生必须登记上课而产生关系。

EntityBean关系的实现方法对于BMP和CMP是不同的。BMP需要编码来实现关系,而CMP是由容器来处理关系的(开发者必须在部署描述符中定义关系)。因此,EntityBean的关系通常是指采用CMP的关系。

容器管理的持久性(CMP)

容器管理的持久性(CMP)是指EJB容器负责处理所有的数据库访问。EntityBean的代码不包含任何数据库访问语句(SQL)。所以Bean的代码不会受到低层存储机制(数据库)的约束。由于这样的灵活性,即使把EntityBean部署到使用不同数据库的不同的服务器上,也不需要修改代码和重新编译。简而言之,CMP大大提高了EntityBean的可移植性。

为了可以自动生成数据库访问代码,容器需要知道EntityBean所代表数据的抽象规则。

抽象数据模式(Abstract Schema)

作为EntityBean的部署描述符的一部分,抽象数据模式定义了EntityBean的持久性字段和关系。抽象数据模式不同于底层数据库的物理表述。抽象是将它和底层的数据库物理模式区分开来(这里指的是把数据库的表和字段直接映射倒EntityBean)。比如关系型数据库的物理规划是指由表和列组成的结构。

你需要在部署描述符中指定抽象数据模式的名称,这个名称会在用EJB QL(Enterptise JavaBean Query Language)写查询的时候被引用。对于CMP,你需要为除了findByPrimaryKey的所有查找方法定义一个对应的EJB QL查询。这个查询将在该查找方法被调用的时候有容器执行。第8章将详细介绍EJB QL。

在你想要编码之前了解一下抽象数据模式的梗概是很有帮助的。下图是一个描绘3个EntityBean之间关系的简单规划。这些关系将在后续章节深入讨论。

图3-1

持久性字段(Persistent Fields)

EntityBean的持久性字段都存储在地层的数据存储设备中。它们共同组成了Entity Bean的状态。在运行时,EJB容器自动地在数据库和EntityBean之间同步这些状态。在部署的时候,典型情况容器会把EntityBean映射为数据库中的一张对应表而持久性字段映射为表的字段(column列)。

例如:一个EntityBean CustomerEJB可能有firstName,lastName,phone和emailAddress等持久性字段。在CMP中它们都是虚拟的,它们都在抽象数据模式中以访问方法(getters和setters)的形式声明,不需要像BMP一样在EntityBean中把它们声明为实例变量。

关系字段(Relationship Fields)

一个关系字段就像数据库的一个外键,它识别一个关联的Bean。和持久性字段一样,关系字段在CMP中也是虚拟的并以访问方法形式定义。但是关系字段并不表示Entity Bean的状态。关系字段会在CMR的方向一节进一步讨论。

CMR(Container-ManagedRelationships)分类

CMR可以分为四类:

一对一:一个EntityBean的实例对应另一个EntityBean的单个实例。例如,对于一个存储箱存放一个小部件的物资仓库模型,StorageBinEJB和WidgetEJB的关系就是一对一。

一对多:一个EntityBean实例对应另一个EntityBean的多个实例。例如,一张订单可以有很多的明细项目,在order应用中,OrderEJB和LineItemEJB的关系就是一对多。

多对一:呵呵,把上面的例子再读一边就好了:)

多对多:两个EntityBean中任一EntityBean的单个实例都可能对应另一个的多个实例。例如:在学校里,一门课有很多学生上,而每一个学生都不会只上一门课。因此在enrollment应用中,CourseEJB和StudentEJB的关系就是多对多。

CMR的方向

只有两种可用的方向:单向或者双向(呵呵:)。在双向的关系中,每一个EntityBean都有对另一个EntityBean引用的关系字段。通过关系字段,EntityBean可以访问相关的EntityBean对象。如果一个EntityBean有关系字段,我们通常会说它“知道”它的关联对象。就像OrderEJB“知道”它的明细项LineItemEJB,如果同时LineItemEJB也“也知道”自己所属的订单OrderEJB,那么它们的关系就是双向的。

在单向的关系中,只有一个EntityBean中有对另一个EntityBean引用的关系字段。像订单明细LineItemEJB中有一个产品的关系字段引用ProductEJB,而ProductEJB中并没有对LineItemEJB引用的关系字段。就是说LineItemEJB“知道”它要卖哪个ProductEJB而ProductEJB却还是SB一样被谁卖了都不晓得。

EJB QL查询通常需要通过这些关系取得数据。关系的方向决定了查询可以从哪个EJB向另一个相关的EJB取得数据。例如一个查询可以通过LineItemEJB取得对应的ProductEJB代表的产品数据,但却不能从ProductEJB查询到有哪些订单明细项出卖过它。而对于双向关系想怎么玩就怎么玩吧,大家都是成年人嘛,只要不是太过分哦:)

何时需要EntityBean

☆ Bean代表一个商务实体而不是一个过程。例如表示信用卡的CreditCardEJB要做成EntityBean,而信用卡核实的VerifierEJB就只能做成会话Bean。

☆ Bean的状态是需要持久存储的。如果Bean的实力结束了或者J2EE服务器关闭,它的状态依然存在,只是回到向数据库这样的存储设备睡觉去了。

四、Message-Driven Bean

注:因为Message-DrivenBean依赖于JMS(Java Message Serviece,Java消息服务)技术,所以本节包含The Java Message Service Tutorial的一些内容。如果要全面了解它们的工作原理请参考上书,下在地址:

http://java.sun.com/products/jms/tutorial/index.html

Message-DrivenBean的构造

Message-DrivenBean是一种可以让应用程序一部处理消息的企业Bean。它以JMS消息监听者的方式工作,很像一个事件监听者,只是用消息代替了事件。消息的发送者可以是任意J2EE构件——应用程序客户端、别的企业Bean或者Web应用——或者一个JMS应用程序或者别的非J2EE系统。

Message-DrivenBean现在只能处理JMS消息,不过将来一定可以处理任意类型的消息。

第7章会详细讨论Message-DrivenBean。

与会话Bean、EntityBean的不同之处

Message-DrivenBean与另外两种企业Bean最明显的区别是客户端访问Message-DrivenBean不需要通过接口(接口定义将在下一节介绍)。就是说它只需要一个Bean类文件。

Message-DrivenBean在有些方面和无状态会话Bean相似:

☆ 它的实例不保持数据或者与特定客户端的会话状态

☆ 一个Message-DrivenBean的所有实例都是等价的,容器可以把消息分给任何一个实例处理。容器可以通过实例池实现同时处理多个消息流。

☆ 单个Message-DrivenBean可以处理多个客户端发送的消息

Message-DrivenBean并不是任何状态都保持,在处理客户端发送的消息期间它也通过实例变量保持一些状态,例如:JMS连接,数据库连接或者对企业Bean的引用等。

当一个消息发送到J2EE服务器端,容器调用Message-DrivenBean的onMessage方法来处理该消息。该方法通常把收到的消息造型为五种JMS消息之一然后根据该应用的商业逻辑处理收到的消息。该方法也可以调用别的辅助方法或者调用一个会话Bean或者EntityBean的方法来处理消息中的信息或把消息存储到数据库。

消息也许和事务上下文一起发送给Message-DrivenBean,这样onMessage方法中的所有操作都会被当作同一个事务或其中的一部分来处理。如果处理过程被回滚,消息就必须重发。详见第7章

何时需要Message-DrivenBean

用会话Bean和EntityBean也可以发送和接收JMS消息,但它们是同步的。而很多时候同步并不是必要的,这时候同步反而会强占很多服务器资源,这样我们可以采用异步方式来处理以减少资源消耗。需要异步消息处理就是用Message-DrivenBean。

五、定义客户端访问接口

注:本节内容不适用于Message-DrivenBean,因为它不需要通过接口访问:)

客户端只能通过会话Bean或者EntityBean的接口中定义的方法来访问它们。接口就相当于一个企业Bean的客户端视图。而企业Bean的方法实现、部署描述符设置、抽象数据模式和数据库访问对客户端都是透明的。设计优良的接口可以使J2EE应用程序的开发和维护更简单。优雅的接口不仅避免了客户端了解EJB层的复杂性,同时它们也使EJB内部实现的修改不会影响到客户端。甚至你把原来用BMP实现的EntityBean改为用CMP实现也不需要改变客户端的代码。但是如果你修改了接口中的方法声明,那么没办法客户端也只有作相应的修改了。就向地下工作者的联系暗号,如果上线的暗号变了,而下线还用旧暗号是不可能在联系上的了。因此,为了尽量使你的客户端不受EJB更改的影响,必须谨慎的设计接口。

在设计J2EE应用程序的时候,你一开就应该要讨论问题之一就是企业Bean允许客户端访问的方式:远程或者本地访问。

远程访问

一个企业Bean的远程客户端有以下特征:

☆ 它可以运行在一个与它访问的企业Bean不同的机器和一个不同的Java虚拟机(Java virtual machine JVM)环境中。但并不是必须的。

☆ 它可以是一个Web应用或者一个J2EE的应用程序客户端,也可以是其他的企业Bean。

☆ 对于远程客户端,企业Bean的位置是透明的。

要创建一个可以远程访问的企业Bean你必须为它编写一个Remote接口和一个Home接口。Remote接口定义商业方法,不同的企业Bean有不同的商业方法(这个是废话,因为企业Bean是根据商业逻辑划分的实体或者处理过程)。如BankAccountEJB有两个名字为debit(借)和credit(贷)的商业方法。Home接口定义企业Bean的生命周期方法create和remove方法。对EntityBean,Home接口还定义查找方法(finder)和家族(home)方法。查找方法用来定位EntityBean。家族方法是被调用以操作所有的EntityBean实例的,就是说这些方法的调用对于对应EntityBean的实力都起作用。下图是由接口组成的企业Bean的客户端视图。

图3-2

本地接口

企业Bean的本地客户端特征:

☆ 它必须和被调用的企业Bean在同一个java虚拟机环境中。

☆ 它可以是Web应用或者其他的企业Bean。

☆ 对于本地客户端,企业Bean的位置是不透明的。

☆ 它们通常是访问CMP的其他EntityBean。(一般是会话Bean。在J2EE设计模式一书中描述的会话外观模式就是这种情况,用会话Bean调用EntityBean,以免客户端反复调用EntityBean的细粒度数据方法。)

要创建一个允许本地访问的企业Bean,你必须编写一个Local接口和一个Local Home接口。相应的,Local接口定义商业方法,Local Home接口定义企业Bean的生命周期和查找方法(没有家族方法?)。

Local接口和CMR(Container-Managed RelationShips)

如果一个企业Bean是一个CMR的靶子,那么它必须有Local接口。关系的方向决定企业Bean是不是该关系的靶子。例如在图3-1中,ProductEJB就是它和LineItemEJB的单向关系中的靶子,应为LineItemEJB在本地访问ProductEJB,所以ProductEJB必须有Local接口。而LineItemEJB和OrderEJB也必须实现Local接口,因为它们的关系是双向的,也就是说都是该关系的靶子。

因为参与CMR的企业Bean(只有CMP可以参与CMR所以实际上这里只可能是CMP)是本地访问关系的靶子,所以它们必须被打包在同一个EJB JAR文件里。本地访问的重要好处是提高了性能——本地调用同长比远程调用要快得多。

两种访问方式的抉择

我们可以根据一下因素来决定是选用远程访问还是本地访问:

☆ CMR:如果一个企业Bean是CMR的靶子,那么它必须实现本地访问。

☆ 企业Bean之间的关系是紧耦合还是松耦合:紧耦合的企业Bean互相依赖。例如一个订单必须有一条或者多条商品条目,这些条目脱离订单就毫无意义。表示它们的EntityBean OrderEJB和LineItemEJB是典型的紧耦合模型。紧耦合关系的企业Bean一般都在同一个商业逻辑单元里,而且他们通常会用频繁的相互调用。所以最好考虑在紧耦合的企业Bean之间使用本地访问,会大大的提高性能。

☆ 客户端的类型:如果企业Bean是被J2EE应用程序客户端访问,那么它必须允许远程访问。在生产环境中,客户端大多数情况下运行在和J2EE服务器不同的机器中。如果客户端是Web应用或者其他的企业Bean,那么它们也可以和被访问的企业Bean部署在同一个环境中,访问方法也取决于如何部署应用程序。

☆ 组件部署:J2EE应用程序是可升级的,因为服务器端的组件可以本分布在多个不同的机器中。例如一个分布式应用程序的Web应用可以运行在与它调用的企业Bean不同的服务器中。在这种分布式场景下,企业Bean必须允许远程访问。

如果你还不能确定使用哪种访问方式,那就选择远程访问。它可以让你的应用程序更灵活——在以后你可以任意分布部署你的应用程序组件以适应不断增长的需求。

虽然很少这样做,但企业Bean也可以两种访问方式同时与允许,这样它就要同时实现Remote和Local两组接口。

性能和访问方式

因为诸如网络反应时间之类的因素,远程调用会比本地调用慢。另一方面,如果在不同的服务器上分布组件,又可以提高应用程序的整体性能。这两种描述都是概念性的,实际情况中性能在不同的运行环境中会出现很大的变化。然而你应该谨记你的设计会给应用程序的性能造成什么样的影响。

方法参数和访问方式

访问方式会影响客户端调用的企业Bean方法的参数和返回值的类型。

隔离

远程调用中的参数是传值的,它们是对象的拷贝。但是本地调用的参数是传引用的,和一般的Java方法调用一样。

远程调用的形式参数核实参事相互隔离的。在调用过程中,客户端和企业Bean对不同对象拷贝操作。如果客户端改变了对象,企业Bean中的对应对象并不会跟着改变。这个隔离层可以保护企业Bean不会被客户端以外的修改数据。(这也造成了值对象模式的一些弊端,如不可以同步刷新而可能造成脏数据,见J2EE核心模式,值对象模式。可见有其利必有其弊:)

在本地调用的过程中,因为引用同一个对象,客户端和企业Bean都可能修改都操作同一个对象。但是一般情况下,请不要以来这种效果来实现什么功能,因为可能以后有一天你要分布部署你的组件,而需要用远程调用来替换本地调用。

数据访问粒度

因为远程调用会比本地调用慢,远程方法的参数应该设计成粗粒度对象。由于粗粒度对象比细粒度对象包含更多的数据,所以也减少了调用次数。(这也是值对象模式的初衷,下面的CustomerDetials对象就是值对象的一个例子)

例如,假设CustomerEJB是通过远程访问的。那么它就可能只有一个getter方法用来返回一个CustomerDetails对象。但是如果它是通过本地访问的,那么它就可以为每一个企业Bean的字段提供一个getter方法:getFirstName、getLastName、getPhoneNumber等等。因为本地调用要比远程调用快很多,这些多次getter方法的调用并不会明显的影响性能。(注意这里说的这些getter方法都是指在Remote或者Local接口里声明的客户端可访问的方法)

六、企业Bean的“内容”

要创建一个企业Bean,你必须提供一下这些文件:

☆ 部署描述符文件:一个描述企业Bean的持久性类型和事务属性等信息的XML文件。如果你是按照新建企业Bean向导的步骤来创建的话,Deploytool工具可以有效的创建部署描述符。

☆ 企业Bean的类文件:实现节口中定义的方法。

☆ 接口:如上节所述,对于远程调用需要实现Remote和Home接口,而本地调用需要实现Local和Local Home接口。而这些Message-DrivenBean是例外,它不需要任何接口。

☆ 辅助类:企业Bean类需要的其他类文件,像异常类和工具类等等。

你必须把这些文件打包进保存企业Bean的模块一个EJB JAR文件中。一个EJB JAR文件是可移植的并且可以在多个J2EE应用程序中使用。要装配拟议J2EE应用程序,你需要将一个或多个像EJB JAR文件一样的模块打包一个保存整个应用程序的存档EAR文件中。当你部署了这个EAR文件,企业Bean也同时被部署到J2EE服务器中。

七、企业Bean的命名约定

因为企业Bean是有多个部分组成,让应用程序遵循一定的命名约定是很有用的。下表示一个例子:

表 3-2 企业名的命名约定

项目

约定

实例

企业Bean命名(DD)

<name>EJB

AccountEJB

EJB的存档文件JAR命名(DD)

<name>JAR

AccountJAR

企业Bean主类命名

<name>Bean

AccountBean

Home接口命名

<name>Home

AccountHome

Remote接口命名

<name>

Account

Local home接口命名

Local<name>Home

LocalAccountHome

Local接口命名

Local<name>

LocalAccount

抽象数据模式命名(DD)

<name>

Account

DD表示该项目是部署描述符文件里的项目。

其实命名约定指是一个习惯问题,不是什么要求或者必须遵守的技术规范。

八、企业Bean的生存周期

企业Bean的生命周期可以划分为几个阶段,不过不同类型的企业Bean如会话Bean,EntityBean和Message-DrivenBean都有不同的生命周期。下面的描述提到的方法都是围绕着接下来两章的示例代码展开的。如果你没有做个企业Bean的开发,可以跳过本节看一下示例代码先。

有状态会话Bean的生命周期:

图3-3显示了一个有状态会话Bean的生命周期中的三个阶段:从不存在到准备就绪到钝化,然后原路返回。客户端调用create方法(该方法在Home或者Local Home节口中声明)就开始了一个有状态会话Bean的生命周期(在此之前是Does Not Exist,不存在阶段)。EJB容器产生一个Bean的实例然后调用Bean类的setSessionContext和ejbCreate(和Home或者Local Home节口中被调用的create方法对应的ejbCreate)方法。这时该Bean实例进入准备就绪状态(Ready),它的商业方法可以被客户端调用了。

图 3-3 有状态会话Bean的生命周期

在就绪状态,EJB容器可能会把该Bean实例从内存中移出到外存以钝化该实例(EJB容器一般采用最近最少使用原则来钝化内存中的Bean实例)。其实就是把内存中EJB容器认为暂时不会用到的Bean实例转移到外存中,以提高内存使用效率,这好像是支持多线程操作系统的内存管理。在钝化Bean实例之前,EJB容器会先调用ejbPassivate方法,所以你如果想在钝化之前做什么动作的话就可以在该方法里实现。如果钝化期间有客户端调用该Bean实例的商业方法,EJB容器将该实例读入内存激活它回到就绪组状态(很有点线程的状态变化哦),同时调用该Bean类的ejbActivate方法。所以你要在Bean实例被激活的时候做点什么动作的话,就在这个方法里动点手脚就OK了:)

在企业Bean的生命周期最后,客户端调用remove(同样是Home或者Local Home接口里的)方法然后容器调用Bean类的ejbRemove方法结束了一个实例的生命。该实例就乖乖的等待垃圾收集器的召唤了,不知道等待黑白无常的心情如何,哎你认命吧,Bean兄弟。

在这些生命周期方法中,你可以在客户端编码调用的只有两个:create和remove方法(呵呵,上面说过一遍了),如图3-3中所示的其他方法都是由EJB容器来调用的。例如,ejbCreate方法在Bean类里,允许你在Bean类实例化后执行一些特定的操作,比如你想让它自己实例化后就报个到,喊一声“我来了”,那把代码塞在这个方法里面就没错了。或者你是想让它跟要访问的数据库打个招呼,建个连接也在这里。

无状态会话Bean的生命周期

因为无状态会话Bean不需要保存任何状态信息,所以它不需要钝化,在不用的时候直接干掉就好了。所以它的生命周期只有两个阶段:不存在(Does Not Exist)和就绪(Ready)如下图3-4

图 3-4 无状态会话Bean的生命周期

EntityBean的生命周期

图3-5显示了EntityBean生命周期中的三个阶段:不存在、池状态和就绪。EJB容器创建企业Bean实例后调用它的setEntityContext方法,该方法将实体上下文(Entity Context)传递给Bean实例。然后该实例就被放入同类实例的Bean池中。在这个状态中,实例并没有和特定的EJB对象身份标志关联,池中所有实例都是等价的。EJB容器在把实例变为就绪状态时才为它指定一个特定的标志。(实体Bean映射商业实体,具有分辨实体的唯一主键标志)

有两种方法让实例从池状态到就绪状态。第一种方法,客户端调用create方法,然后EJB容器调用ejbCreate和ejbPostCreate方法。第二种方法,EJB容器调用ejbActive方法。只用在就绪状态下,企业Bean才可以为客户端提供服务。

图 3-5 Entity Bean的生命周期

在EntityBean的生命周期的最后,EJB容器从池中删除Bean实例并调用unsetEntityContext方法。

企业Bean实例在Bean池中并不和任何EJB实体标志关联。以BMP为例,在EJB容器把实例从池状态转到就绪状态时,它并不自动设置实例的主键,所以ejbCreate和ejbActivate方法必须对主键赋值。如果主键不正确,ejbLoad和ejbStore方法(这两个方法在实体Bean种执行和数据库同步数据操作,在BMP中要自己编码实现,BMP一章将详细介绍)就不能正确的在Bean字段和数据库数据之间执行同步。在SavingAccountEJB的例子中,ejbCreate方法通过自身的参数对主键赋值。EjbActivate方法用下面的语句对主键(id)赋值:

id = (String)context.getPrimaryKey();

在池状态下,企业Bean的字段的值是不需要的,你可以在ejbPassivate方法里把这些值置空(赋值为null)一边垃圾收集器可以收集它们。

Message-DrivenBean的生命周期

图3-6显示了Message-DrivenBean生命周期中的两个阶段:不存在和就绪。

EJB容器通常会创建一个Message-DrivenBean的实例池。对其中每一个实例EJB容器完成如下工作:

1. 调用setMessageDrivenContext方法传递上下文对象给实例。

2. 调用ejbCreate方法

图 3-6 Message-Driven Bean的生命周期

和无状态会话Bean一样,Message-DrivenBean也不会被钝化,只有两种状态:不存在和就绪(可以接收消息)。在生命周期的最后容器调用ejbRemove方法,实例就开始等待垃圾收集器的召唤了。