J2EE 组件开发:会话EJB |
作者:佚名 发布时间:2005-04-02 来源:不详
|
===================================
=================================== |
=================================== |
=================================== |
在企业级应用系统内, 说,会话Bean常常起着入口 企业应用系统获取它们想要
|
会话Bean是一种代表客户程序执 点或“前线”EJB的作用。EJB客 利用的功能或服务。
|
行操作的EJB。对于EJB客户程序来 户程序通过与会话Bean的交互,从
|
正如其名字所示,会话 会话只能有一个用户,而且 一旦客户程序结束运行,会
|
Bean类似于一个交互式的会话。 会话Bean也不具备持久化的特点 话Bean也就不再关联到客户程序
|
会话Bean是不共享的,正如交互式 (即它的数据不保存到数据库)。 。
|
会话Bean有两种类型: Stateless Session Bean)
|
有状态会话Bean(Stateful Ses 。
|
sion Bean),无状态会话Bean(
|
对象的状态由实例变量的值描述。对 的会话的状态。鉴于客户程序与Bean的交
|
于有状态会话Bean,实例变量描述了客户程序与Bean 互关系,Bean的状态信息通常称为会话状态。
|
在客户程序与Bean交互期间,状态信 则会话结束,状态信息也不再保留。然而 程序与Bean之间的会话终止,状态信息也
|
息一直有效。如果客户程序运行结束或拆除了Bean, ,状态信息的这种临时性并不成为问题,因为当客户 就没有必要再保存了。
|
无状态会话Bean不为特 Bean的实例变量可以包含状 态信息也就不再保留。除了 ,这使得EJB容器能够把Bea
|
定的客户程序保留会话状态。客 态信息,但状态信息仅在该次调 Bean方法正在执行的时间之外, n的实例分配给任意客户程序。
|
户程序调用无状态Bean的方法时, 用期间有效。当方法调用结束,状 所有无状态Bean的实例都是等价的
|
由于无状态会话Bean支持多个客户程 Bean具有更好的可伸缩性。一般地,对于 话Bean数量少于有状态会话Bean数量。
|
序,对于那些客户程序数量很大的应用,无状态会话 支持同样数量的客户程序来说,应用需要的无状态会
|
一些时候,EJB容器可 无状态会话Bean。因此,无
|
能把有状态会话Bean保存到第二 状态会话Bean比有状态会话Bean
|
级存储设备,但容器永远不会保存 有着更好的性能。
|
Bean的状态无需持久化,只在短期内生存(比如几个小时)。 |
如果满足任意以下条件,使用有状态会话Bean比较合适: |
Bean的状态描述了Bean与特定客户程序的交互。 |
Bean需要保留有关客户程序的信息,且保留期限必须跨越多次方法调用。 |
Bean担负着客户程序到 务视图。
|
应用其他组件之间的中间人的角
|
色,为客户程序提供一个简化的服
|
为提高性能,当Bean具有任意下面的特征之一时,最好选用无状态会话Bean: |
Bean的状态信息不包含任何针对特定客户程序的数据。 |
Bean从数据库提取一组 售的产品信息。
|
客户程序经常使用的只读数据。
|
例如,Bean从数据库提取出本月销
|
无状态会话Bean不在EJB之内保留面 EJB不在本身的域或关联的对象里面保留 信息不是为特定EJB客户程序下一次访问
|
向特定客户程序的状态信息,但这并不意味着这类 任何状态数据,其真实含义是,这类Bean保持的状态 或使用而保留。
|
这种特点使得EJB容器能够更高效、 个客户程序可以使用容器创建的任意一个 构造一个缓冲池,根据客户程序的需求从 一个客户程序。此外,必要时容器能够方 况作出调整。虽然无状态会话Bean可能拥 开发者不能假定这些状态信息的合法性。
|
更灵活地管理无状态会话Bean。在任意时刻,任意一 无状态会话Bean的实例。因此,容器可以为这类实例 缓冲池分配Bean的实例,无需顾虑哪一个实例属于哪 便地创建或拆除Bean的实例,根据应用规模和资源情 有状态信息,但在两次对Bean实例的连续调用之间,
|
图一显示了无状态会话Bean组件构造的基本体系结构。 |
位于图一顶端的是java EnterpriseBean接口派生出 状态会话EJB,比如图一显 ejb.SessionBean接口。无 一显示的someMethod()和an 的构造函数,且不应该实现
|
x.ejb.EnterpriseBean接口,它 了javax.ejb.SessionBean接口 示的MyStatelessSessionEJBean 状态会话EJB实现公用的、非最 otherMethod()。实现会话Bean finalize()方法。
|
是所有EJB的基础接口。从 。公用的、非最终的、非抽象的无 ,必须实现javax. 终的、非抽象的业务方法,比如图 的类必须有一个公用的、不带参数
|
无状态会话Bean上定义 入EJB,它也是SessionBean 了一个EJB会话容器上下文 在Bean实例生存期间,会话
|
的setSessionContext()方法用 接口上定义的第一个由容器调用 的接口,支持会话Bean的实例访 上下文将一直保持与Bean实例的
|
来把一个SessionContext的实例传 的方法。SessionContext对象封装 问容器提供的运行时会话上下文。 关联。
|
对于无状态会话Bean,尽管在Sessio 关键的操作。无状态会话Bean必须定义一 Bean的实例时将调用这个方法。容器决定 缓冲池,也有可能是因为它接收到了客户 实现的特殊的构造函数或初始化方法。
|
nBean接口中没有定义ejbCreate()方法,但它是一个 个返回值为void的ejbCreate()方法,容器准备创建 创建Bean的实例可能是因为它要构造一个Bean实例的 程序的请求。因此,ejbCreate()方法属于一种由EJB
|
当容器决定不让Bean的 ejbRemove()方法。对于无 决定,不受EJB客户程序的
|
实例继续处理客户程序的请求时 状态会话Bean,何时调用Bean实 任何影响。
|
,它就会调用Bean实例的 例的ejbRemove()方法由容器单独
|
有状态会话Bean在EJB Bean的状态信息是指保存在 据。当一个EJB客户程序在 则状态信息将被保留,下一
|
之内保留的状态信息与EJB客户 Bean实例的域里面的数据,以及 某一时刻访问一个有状态会话Be 次Bean再次被访问时,Bean的实
|
程序有着明确的关系。有状态会话 Bean实例持有的各种对象里面的数 an,且改变了该Bean实例的状态, 例将使用原先保存的状态信息。
|
对于有状态会话Bean,容器承担着更 拆除有状态会话Bean直接关系到服务器端 可能决定把一个或者多个有状态会话Bean 重新空闲,或出现了客户程序的请求,被 状态会话Bean时,开发者必须考虑更多的
|
多的Bean管理方面的责任。实际上,客户程序创建或 Bean实例的创建和拆除。此外,当资源紧张时,容器 串行化(也就是钝化)到持久性存储设备,一旦资源 钝化的Bean必须激活并转入活动内存。因此,设计有 问题。
|
图二显示了有状态会话Bean组件构造的基本体系结构。 |
公用的、非最终的、非 ,必须实现SessionBean接 会话EJB也实现公用的、非 anotherMethod()方法。实 实现finalize()方法。最后 口,使得Bean能够收到某些
|
抽象的有状态会话Bean,如图二 口。SessionBean接口从Enterpr 最终的、非抽象的业务方法,比 现会话Bean的类必须有一个公用 ,有状态会话Bean可以实现java 事务管理方面的事件通知,但这
|
显示的MyStatefulSessionEJBean iseBean接口派生。另外,有状态 如图二显示的someMethod()方法和 的、不带参数的构造方法,且不应 x.ejb.SessionSynchronization接 是可选的。
|
由于状态信息对于有状态会话Bean的 话Bean可以定义一个或者多个ejbCreate( 法的返回值类型是void。传递给这类方法 ejbCreate()。与无状态会话Bean上的ejb 方法绑定到EJB客户程序,EJB客户程序将 无状态会话Bean,在调用任何ejbCreate( setSessionContext()方法。
|
重要性,创建Bean时初始化操作也很重要。有状态会 ...)方法,这些方法带有零个或者多个输入参数,方 的具体参数由应用本身决定,但方法的名字必须是 Create()调用不同,有状态会话Bean的ejbCreate() 一直使用特定的EJB实例。另外也请注意,正如对于 )方法之前,容器将调用有状态会话Bean的
|
如果有状态会话Bean的 Bean继续处理请求。另外, 。
|
ejbRemove()方法被调用,则表 会话最大超时时间到达时,容器
|
明对应的客户程序已经决定不让该 也会调用Bean的ejbRemove()方法
|
设计有状态会话Bean的 所指出的,容器钝化某个Be 设备。容器之所以钝化Bean ”算法确定应该钝化哪些Be
|
过程中,有时最重要的事情就是 an时,它将串行化Bean的内容, 是因为内存资源不足。通常,容 an的实例(当然,实际所用的算
|
Bean的钝化和激活操作。正如前面 并把这些信息写入某个持久性存储 器通过某种形式的“最近最少使用 法由具体的平台决定)。
|
在钝化Bean的实例之前,容器会调用 现这个方法。在ejbPassivate()方法中, 数据库连接和打开的文件句柄。ejbPassi 能够被容器钝化。
|
Bean的ejbPassivate()方法,有状态会话Bean必须实 我们应该清除所有不能串行化和持久化的资源,比如 vate()方法执行完毕之后,所有仍未关闭的对象应该
|
如果出现了对已经被钝 。此时,容器读取以前被串 。一旦完成对Bean状态的重 ejbActivate()方法中,我 方面,如果Bean保存在持久 平台可能会从持久化存储中
|
化的Bean的请求,或者有了空闲 行化的Bean状态数据,并在活动 新构造,容器将调用有状态会话 们应该重新构造出那些在ejbPas 性存储设备中超过了一定的时间 删除原先保存的Bean。
|
的资源,容器将激活被钝化的Bean 内存中用该状态数据重新构造Bean Bean的ejbActivate()方法。在 sivate()方法内关闭的资源。另一 (一个可配置的限制时间),运行
|
前面我们了解了如何构造服务器端的 件提供的服务。客户程序与会话Bean的交 话Bean的Remote接口。会话Bean的Home接 与此相对应,会话Bean的Remote接口提供
|
会话Bean组件,接下来看看客户程序如何利用这些组 互主要通过两种接口完成:会话Bean的Home接口,会 口主要作为一个创建会话Bean对象引用的工厂使用, 了分布式会话Bean对象的应用层操作的客户端接口。
|
会话Bean的Remote接口定义了一组应 。远程接口描述了客户程序所看到的EJB 器。
|
用层的、可在特定会话Bean上调用的、分布式的操作 ,正如RMI远程接口描述了客户程序所看到的RMI服务
|
图三描述了构造和使用分布式会话EJB Remote客户端接口的基本逻辑结构。 |
所有指向分布式EJB对 javax.ejb.EJBObject接口 java.rmi.Remote接口为远 户端使用了容器提供的Stub 架程序再把调用委托给实际 对于哪一个Bean实例被选用 即客户程序对EJB远程接口 调用。
|
象的远程应用层接口,比如图三 派生,而EJBObject接口又从jav 程EJB接口增加了分布式RMI支持 程序,Stub程序把调用传递到服 的服务器端EJB组件,比如图三 以及调用如何在服务器端传递负 中的方法的调用,最终导致对应
|
显示的MySessionEJB,必须从 a.rmi.Remote接口派生, 。从实现原理来看,远程接口在客 务器端骨架程序(Skeleton),骨 显示的MySessionEJBean。EJB容器 有最终责任,但结果总是一样的, 的服务器端EJB组件实例上的方法
|
每一个会话EJB组件必 法,比如MySessionEJBean. 例如MySessionEJB.someMet 必须声明它可以抛出java.r 外,还有一组继承自EJBObj
|
须有一个远程EJB接口。对于EJB someMethod(),EJB客户端远程 hod()。由于分布式特性的要求 mi.RemoteException异常。在远 ect接口的方法可供调用。
|
服务器端组件上的每一个应用层方 接口上必须定义一个对应的方法, ,应用层远程接口中的每一个方法 程EJB对象上,除了应用层方法之
|
会话Bean的客户程序利用Home接口创 用都通过远程会话Bean接口对象实现。客 这种方式使得容器有必不可少的机会处理 绕过了Home接口,直接使用通过其他手段 可能导致线程方面的问题,因为不能有一 。
|
建会话Bean的引用以及拆除这些引用。当然,这些引 户程序应该总是通过Home接口创建EJB的引用,因为 EJB实例资源分配方面的工作。反之,如果客户程序 获得的EJBObject对象句柄,比如通过JNDI查找,则 个以上的客户程序线程使用同一个服务器端Bean实例
|
图四显示了构造会话Bean Home接口 我们利用JNDI获取EJB Home接口对象的句 MySessionEJBHome接口从标准的javax.ej 由Stub程序实现,Stub程序与服务器端的 的实例,并把调用传递给会话Bean的实例
|
的基本体系结构,以及客户程序如何利用这些接口。 柄,比如图四显示的MySessionEJBHome。 b.EJBHome接口派生。EJB Home接口对象也在客户端 Skeleton程序、容器通信,容器创建或拆除会话Bean 。
|
EJB客户程序创建一个 通过JNDI完成。在J2EE环境 ,并通过InitialContext.l InitialContext.lookup() 述器中定义。元素定义的内 信息等,如下面的例子所示
|
新的EJB引用时,它首先要获得 下,客户程序能够使用InitialC ookup()调用方便地查找EJB Hom 的名字可以引用一个元素,该元 容包括引用名称、被引用EJB的 :
|
一个EJB Home接口句柄。这个任务 ontext对象的默认无参数构造函数 e接口的句柄。传递给 素在客户程序的XML格式的部署描 类型、Home接口信息、Remote接口
|
Home接口定义了一个或 对于EJB类里面定义的每一 create(...)方法。create( ejbCreate(...)方法需要的 接口的实例(例如MySessio mi.RemoteException异常和 ejbCreate(...)方法定义的
|
者多个create()方法,这些方法 个ejbCreate(...)方法,Home接 ...)方法可以包含零个或者多个 初始化参数类型有关,但这些cr nEJB)。create(...)方法还必 javax.ejb.CreateException异 应用层异常。
|
代表着创建EJB对象的各种方式。 口上必须定义一个对应的 输入参数,与对应的 eate(...)方法都返回一个EJB远程 须能够抛出java.r 常,能够抛出各种为对应的
|
由于无状态会话Bean只 口也只能定义一个不带参数 器从缓冲池取出一个Bean的 对于有状态会话Bean,容器 Home接口create(...)方法
|
定义一个不带参数的ejbCreate( 的create()方法。在无状态会话 实例,不一定导致容器立即调用 将调用Bean实例的ejbCreate(.. 的参数。
|
)方法,无状态会话Bean的Home接 Bean上调用create()方法只导致容 Bean的ejbCreate()方法。但是, .)方法,并在调用时提供所有传入
|
这里我们要分析的是一 包含一个名为StatefulFund 数据仅在会话期间有效,即
|
个有状态会话Bean应用的例子。 ManagerEJB的有状态会话EJB。 不能持久保存。应用包含如下文
|
这是一个很简单的资金管理程序, 应用支持资金存入和提取操作,但 件:
|
会话Bean类:StatefulFundManagerEJB.java |
Home接口:StatefulFundManagerHome.java |
远程接口:StatefulFundManager.java |
此外,本例还包含一个 代码和应用EAR文件可以从
|
InsufficientBalanceException 本文后面下载。
|
.java文件定义的异常。完整的源
|
本例的会话Bean名为StatefulFundMa StatefulFundManagerEJB满足以下要求:
|
nagerEJB。和所有其他会话Bean一样,
|
SessionBean接口定义 setSessionContext()方法 SessionBean接口声明了它 都是空的。
|
了ejbRemove()、ejbActivate() 。StatefulFundManagerEJB不需 们,因此必须实现它们。在Stat
|
、ejbPassivate()和 要用到这些方法,但由于 efulFundManagerEJB中,这些方法
|
EJB在容器之内运行, 。创建EJB实例时,应用的
|
客户程序不能直接创建Bean的实 操作步骤如下:
|
例,只有容器才能创建EJB的实例
|
客户程序调用Home对象上的create()方法: |
StatefulFundManager manager = ho
|
me.create(2000);
|
容器调用StatefulFund 的实现代码:
|
Manager上合适的ejbCreate()方
|
法。下面是本例ejbCreate()方法
|
public void ejbCreate(double am
|
ount) throws CreateException {
|
throw new Cre
|
ateException("资金数量错误")
|
;
|
ejbCreate()方法初始 初始化资金数量(amount) 异常。ejbCreate()方法应
|
化Bean的状态。例如,上面的ej 。当输入参数非法时,ejbCreat 当满足如下要求:
|
bCreate()方法用参数传入的数值 e()方法通常抛出CreateException
|
如Bean支持远程访问,参数类型必须是合法的RMI类型。 |
会话Bean的根本目的是为客户程序提 对象引用调用业务方法。从客户端看来, 的会话Bean上执行。下面的代码片断显示 用业务方法:
|
供业务方法,客户程序通过create()方法返回的远程 业务方法似乎在本地执行,但实际上业务方法在远程 了StatefulFundManagerTestClient客户程序如何调
|
if (e.getSource() == addFunds) { |
manager.addFunds
|
(Double.parseDouble(amount.g
|
etText()));
|
String currencyOut = currenc
|
yFormatter.format(manager.getBalance());
|
status.setText(msg + currencyOut); |
if (e.getSource() == withdrawFunds) { |
// 调用管理器EJB上的withdrawFunds方法 |
manager.withdrawFunds(Double
|
.parseDouble(amount.getText()));
|
String currencyO
|
ut = currencyFormatter.forma
|
t(manager.getBalance());
|
status.setText(msg + currencyOut); |
下面是StatefulFundManagerEJB实现的其中两个业务方法: |
public void addFunds(double amount) { |
public void withdrawFunds(dou
|
ble amount) throws
|
InsufficientBalance
|
Exception {
|
if (this.amount < amount) { |
throw (new InsufficientBa
|
lanceException());
|
方法名字不能与EJB体系定义的标准 ejbActivate()。
|
方法冲突,例如,方法名字不能是ejbCreate()或
|
如果Bean允许远程访问,则参数和返回值必须是合法的RMI类型。 |
对于会话Bean来说,Home接口的目的 的每一个create()方法对应着Bean类中的 定义的ejbCreate()方法,请比较Home接
|
是定义供客户程序调用的create()方法。Home接口中 一个ejbCreate()方法。前面我们已经看到了Bean类 口StatefulFundManagerHome定义的create()方法:
|
import java.rmi.RemoteException; |
import javax.ejb.CreateException; |
import javax.ejb.EJBHome; |
public interface St
|
atefulFundManagerHome extend
|
s EJBHome {
|
StatefulFundManag
|
er create(double amount) thr
|
ows RemoteException,
|
ejbCreate()方法和create()方法有 中定义create()方法的规则如下:
|
着相似的特征,但两者又有重要的区别。在Home接口
|
create()方法中参数的数量和类型必 。
|
须与对应的ejbCreate()方法的参数数量和类型匹配
|
create()方法的参数和返回值必须是合法的RMI类型。 |
create()方法返回EJB的远程接口类
|
型(ejbCreate()方法返回void)。
|
create()方法的throws ejb.CreateException。
|
子句必须包含java.rmi.RemoteE
|
xception和javax.
|
远程接口定义了供客户程序调用的业 StatefulFundManager,定义如下:
|
务方法。StatefulFundManagerEJB的远程接口是
|
import javax.ejb.EJBObject; |
import java.rmi.RemoteException; |
public interface StatefulFundMa
|
nager extends EJBObject {
|
public void addFu
|
nds(double amount) throws Re
|
moteException;
|
public void withdrawFunds(dou
|
ble amount)
|
throws
|
InsufficientBalanceException
|
, RemoteException;
|
public double getBalance() th
|
rows RemoteException;
|
远程接口中定义的每一个方法必须与EJB类里面定义的一个方法匹配。 |
throws子句必须包含java.rmi.RemoteException。 |
编译好Bean的代码后,还应该把Bean 素里面可以包含一个元素,元素可以包含 署信息。
|
封装成JAR文件。EJB模块部署描述器的根元素是。元 一组元素。每一个元素描述一个会话Bean的配置和部
|
下面给出了本例ejb-ja 文件内的唯一名称、Bean的
|
r.xml文件的主要内容。元素定 类名称和接口名称、会话Bean的
|
义了会话Bean的元数据、EJB JAR 类型、安全信息,事务信息等。
|
一般地,配置和部署会话Bean时还需 以用平台提供的GUI部署工具编写,此处
|
要面向特定平台的部署描述器文件,这种文件通常可 不再赘述,请参见本文下载代码中的例子。
|
客户程序是一个Swing程序,运行界 金”增加资金余额,点击“取出资金”减
|
面如图五所示。在文本框中输入数字,点击“存入资 少资金余额。
|
在下面的说明中,我们 成StatefulFundManager.ea 台的相关文档。
|
假定运行平台是SUN的J2EE SDK r文件。关于如何把应用打包成E
|
、Windows 2000,且应用已经打包 AR文件的详细说明,请参见开发平
|
首先执行j2ee启动J2EE服务器。然后,在另一个命令窗口中执行deploytool。在deploytool中,选择菜单“File-->Open”,打开StatefulFundManager.ear文件。接着,选择菜单“Tools --> Deploy”,部署应用。出现部署提示时,选中“Return Client JAR”检查框。 |
部署完毕后,在一个命令窗口中进入 StatefulFundManagerClient.jar文件) StatefulFundManagerClient.jar。然后
|
StatefulFundManager.ear文件( 所在目录,把APPCPATH环境变量设置成 ,执行下面的命令启动客户程序:
|
runclient -client St -textauth
|
atefulFundManager.ear -name
|
StatefulFundManagerTestClient
|
输入用户名字j2ee,输入密码j2ee,
|
即可启动程序。客户程序的运行界面请参见图五。
|
|
|
|
|
|