帝国软件
  设为首页 加入收藏 关于我们
 
解密帝国网站管理系统
栏 目:
 
您的位置:首页 > 技术文档 > JAVA编程
J2EE 组件开发:会话EJB
作者:未知 发布时间:2005-03-12 来源:JSP天空网
作者:仙人掌工作室 本文选自:赛迪网 2002年03月28日

===================================

提纲:

===================================

一、概述

二、无状态会话Bean

三、有状态会话Bean

四、客户端接口

  4.1 Remote接口

  4.2 Home接口

五、实例

  5.1 有状态会话Bean

  5.2 Home接口

  5.3 Remote接口

  5.4 部署描述器

  5.5 客户程序

  5.6 运行

===================================

正文:

===================================

一、概述

在企业级应用系统内,会话Bean是一种代表客户程序执行操作的EJB。对于EJB客户程序来说,会话Bean常常起着入口点或“前线”EJB的作用。EJB客户程序通过与会话Bean的交互,从企业应用系统获取它们想要利用的功能或服务。

正如其名字所示,会话Bean类似于一个交互式的会话。会话Bean是不共享的,正如交互式会话只能有一个用户,而且会话Bean也不具备持久化的特点(即它的数据不保存到数据库)。一旦客户程序结束运行,会话Bean也就不再关联到客户程序。

会话Bean有两种类型:有状态会话Bean(Stateful Session Bean),无状态会话Bean(Stateless Session Bean)。

有状态会话Bean

对象的状态由实例变量的值描述。对于有状态会话Bean,实例变量描述了客户程序与Bean的会话的状态。鉴于客户程序与Bean的交互关系,Bean的状态信息通常称为会话状态。

在客户程序与Bean交互期间,状态信息一直有效。如果客户程序运行结束或拆除了Bean,则会话结束,状态信息也不再保留。然而,状态信息的这种临时性并不成为问题,因为当客户程序与Bean之间的会话终止,状态信息也就没有必要再保存了。

无状态会话Bean

无状态会话Bean不为特定的客户程序保留会话状态。客户程序调用无状态Bean的方法时,Bean的实例变量可以包含状态信息,但状态信息仅在该次调用期间有效。当方法调用结束,状态信息也就不再保留。除了Bean方法正在执行的时间之外,所有无状态Bean的实例都是等价的,这使得EJB容器能够把Bean的实例分配给任意客户程序。

由于无状态会话Bean支持多个客户程序,对于那些客户程序数量很大的应用,无状态会话Bean具有更好的可伸缩性。一般地,对于支持同样数量的客户程序来说,应用需要的无状态会话Bean数量少于有状态会话Bean数量。

一些时候,EJB容器可能把有状态会话Bean保存到第二级存储设备,但容器永远不会保存无状态会话Bean。因此,无状态会话Bean比有状态会话Bean有着更好的性能。

一般地,在下列情形下,我们可以使用会话Bean:


在任意时刻只有一个客户程序访问Bean的实例。
Bean的状态无需持久化,只在短期内生存(比如几个小时)。


如果满足任意以下条件,使用有状态会话Bean比较合适:


Bean的状态描述了Bean与特定客户程序的交互。
Bean需要保留有关客户程序的信息,且保留期限必须跨越多次方法调用。
Bean担负着客户程序到应用其他组件之间的中间人的角色,为客户程序提供一个简化的服务视图。
Bean管理着多个EJB的工作流程。


为提高性能,当Bean具有任意下面的特征之一时,最好选用无状态会话Bean:


Bean的状态信息不包含任何针对特定客户程序的数据。
Bean从数据库提取一组客户程序经常使用的只读数据。例如,Bean从数据库提取出本月销售的产品信息。


二、无状态会话Bean

无状态会话Bean不在EJB之内保留面向特定客户程序的状态信息,但这并不意味着这类EJB不在本身的域或关联的对象里面保留任何状态数据,其真实含义是,这类Bean保持的状态信息不是为特定EJB客户程序下一次访问或使用而保留。

这种特点使得EJB容器能够更高效、更灵活地管理无状态会话Bean。在任意时刻,任意一个客户程序可以使用容器创建的任意一个无状态会话Bean的实例。因此,容器可以为这类实例构造一个缓冲池,根据客户程序的需求从缓冲池分配Bean的实例,无需顾虑哪一个实例属于哪一个客户程序。此外,必要时容器能够方便地创建或拆除Bean的实例,根据应用规模和资源情况作出调整。虽然无状态会话Bean可能拥有状态信息,但在两次对Bean实例的连续调用之间,开发者不能假定这些状态信息的合法性。

图一显示了无状态会话Bean组件构造的基本体系结构。





位于图一顶端的是javax.ejb.EnterpriseBean接口,它是所有EJB的基础接口。从EnterpriseBean接口派生出了javax.ejb.SessionBean接口。公用的、非最终的、非抽象的无状态会话EJB,比如图一显示的MyStatelessSessionEJBean,必须实现javax.ejb.SessionBean接口。无状态会话EJB实现公用的、非最终的、非抽象的业务方法,比如图一显示的someMethod()和anotherMethod()。实现会话Bean的类必须有一个公用的、不带参数的构造函数,且不应该实现finalize()方法。

无状态会话Bean上定义的setSessionContext()方法用来把一个SessionContext的实例传入EJB,它也是SessionBean接口上定义的第一个由容器调用的方法。SessionContext对象封装了一个EJB会话容器上下文的接口,支持会话Bean的实例访问容器提供的运行时会话上下文。在Bean实例生存期间,会话上下文将一直保持与Bean实例的关联。

对于无状态会话Bean,尽管在SessionBean接口中没有定义ejbCreate()方法,但它是一个关键的操作。无状态会话Bean必须定义一个返回值为void的ejbCreate()方法,容器准备创建Bean的实例时将调用这个方法。容器决定创建Bean的实例可能是因为它要构造一个Bean实例的缓冲池,也有可能是因为它接收到了客户程序的请求。因此,ejbCreate()方法属于一种由EJB实现的特殊的构造函数或初始化方法。

当容器决定不让Bean的实例继续处理客户程序的请求时,它就会调用Bean实例的ejbRemove()方法。对于无状态会话Bean,何时调用Bean实例的ejbRemove()方法由容器单独决定,不受EJB客户程序的任何影响。

三、有状态会话Bean

有状态会话Bean在EJB之内保留的状态信息与EJB客户程序有着明确的关系。有状态会话Bean的状态信息是指保存在Bean实例的域里面的数据,以及Bean实例持有的各种对象里面的数据。当一个EJB客户程序在某一时刻访问一个有状态会话Bean,且改变了该Bean实例的状态,则状态信息将被保留,下一次Bean再次被访问时,Bean的实例将使用原先保存的状态信息。

对于有状态会话Bean,容器承担着更多的Bean管理方面的责任。实际上,客户程序创建或拆除有状态会话Bean直接关系到服务器端Bean实例的创建和拆除。此外,当资源紧张时,容器可能决定把一个或者多个有状态会话Bean串行化(也就是钝化)到持久性存储设备,一旦资源重新空闲,或出现了客户程序的请求,被钝化的Bean必须激活并转入活动内存。因此,设计有状态会话Bean时,开发者必须考虑更多的问题。

图二显示了有状态会话Bean组件构造的基本体系结构。





公用的、非最终的、非抽象的有状态会话Bean,如图二显示的MyStatefulSessionEJBean,必须实现SessionBean接口。SessionBean接口从EnterpriseBean接口派生。另外,有状态会话EJB也实现公用的、非最终的、非抽象的业务方法,比如图二显示的someMethod()方法和anotherMethod()方法。实现会话Bean的类必须有一个公用的、不带参数的构造方法,且不应实现finalize()方法。最后,有状态会话Bean可以实现javax.ejb.SessionSynchronization接口,使得Bean能够收到某些事务管理方面的事件通知,但这是可选的。

由于状态信息对于有状态会话Bean的重要性,创建Bean时初始化操作也很重要。有状态会话Bean可以定义一个或者多个ejbCreate(...)方法,这些方法带有零个或者多个输入参数,方法的返回值类型是void。传递给这类方法的具体参数由应用本身决定,但方法的名字必须是ejbCreate()。与无状态会话Bean上的ejbCreate()调用不同,有状态会话Bean的ejbCreate()方法绑定到EJB客户程序,EJB客户程序将一直使用特定的EJB实例。另外也请注意,正如对于无状态会话Bean,在调用任何ejbCreate()方法之前,容器将调用有状态会话Bean的setSessionContext()方法。

如果有状态会话Bean的ejbRemove()方法被调用,则表明对应的客户程序已经决定不让该Bean继续处理请求。另外,会话最大超时时间到达时,容器也会调用Bean的ejbRemove()方法。

设计有状态会话Bean的过程中,有时最重要的事情就是Bean的钝化和激活操作。正如前面所指出的,容器钝化某个Bean时,它将串行化Bean的内容,并把这些信息写入某个持久性存储设备。容器之所以钝化Bean是因为内存资源不足。通常,容器通过某种形式的“最近最少使用”算法确定应该钝化哪些Bean的实例(当然,实际所用的算法由具体的平台决定)。

在钝化Bean的实例之前,容器会调用Bean的ejbPassivate()方法,有状态会话Bean必须实现这个方法。在ejbPassivate()方法中,我们应该清除所有不能串行化和持久化的资源,比如数据库连接和打开的文件句柄。ejbPassivate()方法执行完毕之后,所有仍未关闭的对象应该能够被容器钝化。

如果出现了对已经被钝化的Bean的请求,或者有了空闲的资源,容器将激活被钝化的Bean。此时,容器读取以前被串行化的Bean状态数据,并在活动内存中用该状态数据重新构造Bean。一旦完成对Bean状态的重新构造,容器将调用有状态会话Bean的ejbActivate()方法。在ejbActivate()方法中,我们应该重新构造出那些在ejbPassivate()方法内关闭的资源。另一方面,如果Bean保存在持久性存储设备中超过了一定的时间(一个可配置的限制时间),运行平台可能会从持久化存储中删除原先保存的Bean。

四、客户端接口

前面我们了解了如何构造服务器端的会话Bean组件,接下来看看客户程序如何利用这些组件提供的服务。客户程序与会话Bean的交互主要通过两种接口完成:会话Bean的Home接口,会话Bean的Remote接口。会话Bean的Home接口主要作为一个创建会话Bean对象引用的工厂使用,与此相对应,会话Bean的Remote接口提供了分布式会话Bean对象的应用层操作的客户端接口。

4.1 Remote接口

会话Bean的Remote接口定义了一组应用层的、可在特定会话Bean上调用的、分布式的操作。远程接口描述了客户程序所看到的EJB,正如RMI远程接口描述了客户程序所看到的RMI服务器。

图三描述了构造和使用分布式会话EJB Remote客户端接口的基本逻辑结构。





所有指向分布式EJB对象的远程应用层接口,比如图三显示的MySessionEJB,必须从javax.ejb.EJBObject接口派生,而EJBObject接口又从java.rmi.Remote接口派生,java.rmi.Remote接口为远程EJB接口增加了分布式RMI支持。从实现原理来看,远程接口在客户端使用了容器提供的Stub程序,Stub程序把调用传递到服务器端骨架程序(Skeleton),骨架程序再把调用委托给实际的服务器端EJB组件,比如图三显示的MySessionEJBean。EJB容器对于哪一个Bean实例被选用以及调用如何在服务器端传递负有最终责任,但结果总是一样的,即客户程序对EJB远程接口中的方法的调用,最终导致对应的服务器端EJB组件实例上的方法调用。

每一个会话EJB组件必须有一个远程EJB接口。对于EJB服务器端组件上的每一个应用层方法,比如MySessionEJBean.someMethod(),EJB客户端远程接口上必须定义一个对应的方法,例如MySessionEJB.someMethod()。由于分布式特性的要求,应用层远程接口中的每一个方法必须声明它可以抛出java.rmi.RemoteException异常。在远程EJB对象上,除了应用层方法之外,还有一组继承自EJBObject接口的方法可供调用。

4.2 Home接口

会话Bean的客户程序利用Home接口创建会话Bean的引用以及拆除这些引用。当然,这些引用都通过远程会话Bean接口对象实现。客户程序应该总是通过Home接口创建EJB的引用,因为这种方式使得容器有必不可少的机会处理EJB实例资源分配方面的工作。反之,如果客户程序绕过了Home接口,直接使用通过其他手段获得的EJBObject对象句柄,比如通过JNDI查找,则可能导致线程方面的问题,因为不能有一个以上的客户程序线程使用同一个服务器端Bean实例。

图四显示了构造会话Bean Home接口的基本体系结构,以及客户程序如何利用这些接口。我们利用JNDI获取EJB Home接口对象的句柄,比如图四显示的MySessionEJBHome。MySessionEJBHome接口从标准的javax.ejb.EJBHome接口派生。EJB Home接口对象也在客户端由Stub程序实现,Stub程序与服务器端的Skeleton程序、容器通信,容器创建或拆除会话Bean的实例,并把调用传递给会话Bean的实例。





EJB客户程序创建一个新的EJB引用时,它首先要获得一个EJB Home接口句柄。这个任务通过JNDI完成。在J2EE环境下,客户程序能够使用InitialContext对象的默认无参数构造函数,并通过InitialContext.lookup()调用方便地查找EJB Home接口的句柄。传递给InitialContext.lookup()的名字可以引用一个元素,该元素在客户程序的XML格式的部署描述器中定义。元素定义的内容包括引用名称、被引用EJB的类型、Home接口信息、Remote接口信息等,如下面的例子所示:


MyStatefulFundManager
Session
StatefulFundManagerHome
StatefulFundManager
StatefulFundManagerBean



Home接口定义了一个或者多个create()方法,这些方法代表着创建EJB对象的各种方式。对于EJB类里面定义的每一个ejbCreate(...)方法,Home接口上必须定义一个对应的create(...)方法。create(...)方法可以包含零个或者多个输入参数,与对应的ejbCreate(...)方法需要的初始化参数类型有关,但这些create(...)方法都返回一个EJB远程接口的实例(例如MySessionEJB)。create(...)方法还必须能够抛出java.rmi.RemoteException异常和javax.ejb.CreateException异常,能够抛出各种为对应的ejbCreate(...)方法定义的应用层异常。

由于无状态会话Bean只定义一个不带参数的ejbCreate()方法,无状态会话Bean的Home接口也只能定义一个不带参数的create()方法。在无状态会话Bean上调用create()方法只导致容器从缓冲池取出一个Bean的实例,不一定导致容器立即调用Bean的ejbCreate()方法。但是,对于有状态会话Bean,容器将调用Bean实例的ejbCreate(...)方法,并在调用时提供所有传入Home接口create(...)方法的参数。

五、实例

这里我们要分析的是一个有状态会话Bean应用的例子。这是一个很简单的资金管理程序,包含一个名为StatefulFundManagerEJB的有状态会话EJB。应用支持资金存入和提取操作,但数据仅在会话期间有效,即不能持久保存。应用包含如下文件:

会话Bean类:StatefulFundManagerEJB.java

Home接口:StatefulFundManagerHome.java

远程接口:StatefulFundManager.java

此外,本例还包含一个InsufficientBalanceException.java文件定义的异常。完整的源代码和应用EAR文件可以从本文后面下载。

5.1 有状态会话Bean

本例的会话Bean名为StatefulFundManagerEJB。和所有其他会话Bean一样,StatefulFundManagerEJB满足以下要求:


实现SessionBean接口。
类定义成public。
类不能定义成abstract或final。
实现一个或者多个ejbCreate()方法。
实现业务方法。
包含一个公用的、不带参数的构造函数。
不能定义finalize()方法。


SessionBean接口定义了ejbRemove()、ejbActivate()、ejbPassivate()和setSessionContext()方法。StatefulFundManagerEJB不需要用到这些方法,但由于SessionBean接口声明了它们,因此必须实现它们。在StatefulFundManagerEJB中,这些方法都是空的。

5.1.1 ejbCreate()方法

EJB在容器之内运行,客户程序不能直接创建Bean的实例,只有容器才能创建EJB的实例。创建EJB实例时,应用的操作步骤如下:

客户程序调用Home对象上的create()方法:

StatefulFundManager manager = home.create(2000);


容器构造EJB的实例。

容器调用StatefulFundManager上合适的ejbCreate()方法。下面是本例ejbCreate()方法的实现代码:

public void ejbCreate(double amount) throws CreateException {

if (amount < 0) {
throw new CreateException("资金数量错误");
}else {
this.amount = amount;
}
}


ejbCreate()方法初始化Bean的状态。例如,上面的ejbCreate()方法用参数传入的数值初始化资金数量(amount)。当输入参数非法时,ejbCreate()方法通常抛出CreateException异常。ejbCreate()方法应当满足如下要求:


访问控制修饰符必须是public。
返回void。
如Bean支持远程访问,参数类型必须是合法的RMI类型。
不能带有static或final修饰符。


5.1.2 业务方法

会话Bean的根本目的是为客户程序提供业务方法,客户程序通过create()方法返回的远程对象引用调用业务方法。从客户端看来,业务方法似乎在本地执行,但实际上业务方法在远程的会话Bean上执行。下面的代码片断显示了StatefulFundManagerTestClient客户程序如何调用业务方法:

if (e.getSource() == addFunds) {
// 调用管理器EJB上的addFunds方法
manager.addFunds(Double.parseDouble(amount.getText()));
// 设置新的余额
String currencyOut = currencyFormatter.format(manager.getBalance());
status.setText(msg + currencyOut);
}
if (e.getSource() == withdrawFunds) {
// 调用管理器EJB上的withdrawFunds方法
manager.withdrawFunds(Double.parseDouble(amount.getText()));
// 设置新的余额
String currencyOut = currencyFormatter.format(manager.getBalance());
status.setText(msg + currencyOut);
}


下面是StatefulFundManagerEJB实现的其中两个业务方法:

public void addFunds(double amount) {
if (amount <= 0){
return;
}
this.amount += amount;
}
public void withdrawFunds(double amount) throws
InsufficientBalanceException {
if (this.amount < amount) {
throw (new InsufficientBalanceException());
}
this.amount -= amount;
}


声明业务方法时应遵从以下规则:


方法名字不能与EJB体系定义的标准方法冲突,例如,方法名字不能是ejbCreate()或ejbActivate()。
访问控制修饰符必须是pubic。
如果Bean允许远程访问,则参数和返回值必须是合法的RMI类型。
不能有static或final修饰符。


5.2 Home接口

对于会话Bean来说,Home接口的目的是定义供客户程序调用的create()方法。Home接口中的每一个create()方法对应着Bean类中的一个ejbCreate()方法。前面我们已经看到了Bean类定义的ejbCreate()方法,请比较Home接口StatefulFundManagerHome定义的create()方法:

import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;

public interface StatefulFundManagerHome extends EJBHome {
StatefulFundManager create(double amount) throws RemoteException,
CreateException;
}


ejbCreate()方法和create()方法有着相似的特征,但两者又有重要的区别。在Home接口中定义create()方法的规则如下:


create()方法中参数的数量和类型必须与对应的ejbCreate()方法的参数数量和类型匹配。
create()方法的参数和返回值必须是合法的RMI类型。
create()方法返回EJB的远程接口类型(ejbCreate()方法返回void)。
create()方法的throws子句必须包含java.rmi.RemoteException和javax.ejb.CreateException。


5.3 Remote接口

远程接口定义了供客户程序调用的业务方法。StatefulFundManagerEJB的远程接口是StatefulFundManager,定义如下:

import javax.ejb.EJBObject;
import java.rmi.RemoteException;

public interface StatefulFundManager extends EJBObject {
public void addFunds(double amount) throws RemoteException;
public void withdrawFunds(double amount)
throws InsufficientBalanceException, RemoteException;
public double getBalance() throws RemoteException;
}


远程接口的定义必须符合如下要求:


远程接口中定义的每一个方法必须与EJB类里面定义的一个方法匹配。
参数和返回值类型必须是合法的RMI类型。
throws子句必须包含java.rmi.RemoteException。


5.4 部署描述器

编译好Bean的代码后,还应该把Bean封装成JAR文件。EJB模块部署描述器的根元素是。元素里面可以包含一个元素,元素可以包含一组元素。每一个元素描述一个会话Bean的配置和部署信息。

下面给出了本例ejb-jar.xml文件的主要内容。元素定义了会话Bean的元数据、EJB JAR文件内的唯一名称、Bean的类名称和接口名称、会话Bean的类型、安全信息,事务信息等。


StatefulFundManagerJAR


StatefulFundManagerBean
StatefulFundManagerBean
StatefulFundManagerHome
StatefulFundManager
StatefulFundManagerEJB
Stateful
Container









一般地,配置和部署会话Bean时还需要面向特定平台的部署描述器文件,这种文件通常可以用平台提供的GUI部署工具编写,此处不再赘述,请参见本文下载代码中的例子。

5.5 客户程序

客户程序是一个Swing程序,运行界面如图五所示。在文本框中输入数字,点击“存入资金”增加资金余额,点击“取出资金”减少资金余额。

5.6 运行

在下面的说明中,我们假定运行平台是SUN的J2EE SDK、Windows 2000,且应用已经打包成StatefulFundManager.ear文件。关于如何把应用打包成EAR文件的详细说明,请参见开发平台的相关文档。

首先执行j2ee启动J2EE服务器。然后,在另一个命令窗口中执行deploytool。在deploytool中,选择菜单“File-->Open”,打开StatefulFundManager.ear文件。接着,选择菜单“Tools --> Deploy”,部署应用。出现部署提示时,选中“Return Client JAR”检查框。

部署完毕后,在一个命令窗口中进入StatefulFundManager.ear文件(StatefulFundManagerClient.jar文件)所在目录,把APPCPATH环境变量设置成StatefulFundManagerClient.jar。然后,执行下面的命令启动客户程序:

runclient -client StatefulFundManager.ear -name StatefulFundManagerTestClient -textauth


输入用户名字j2ee,输入密码j2ee,即可启动程序。客户程序的运行界面请参见图五。

下载文本的代码:J2EESessionEJB_code.zip (http://210.51.0.82:7001/pub/attachment/2002/3/12756.zip)
  
评论】【加入收藏夹】【 】【打印】【关闭
※ 相关链接
 ·浅析J2EE与.NET平台优劣  (2005-03-12)
 ·J2EE如何为你的企业做嫁衣  (2005-03-12)
 ·想免费参加J2EE职业培训吗?  (2005-03-12)
 ·J2EE是组件软件的未来  (2005-03-12)
 ·连接池深入J2EE的连接合用  (2005-03-12)
 ·课程介绍(8):SL-425 体系结构及设  (2005-03-12)
 ·金蝶J2EE应用服务器挑战国外巨头  (2005-03-12)
 ·J2EE组件开发:消息驱动的EJB  (2005-03-12)
 ·Win2000 Server下安装j2ee  (2005-03-12)
 ·IBM发布J2EE 1.3核心软件  (2005-03-12)

   栏目导行
  PHP编程
  ASP编程
  ASP.NET编程
  JAVA编程
   站点最新
·致合作伙伴的欢迎信
·媒体报道
·帝国软件合作伙伴计划协议
·DiscuzX2.5会员整合通行证发布
·帝国CMS 7.0版本功能建议收集
·帝国网站管理系统2012年授权购买说
·PHPWind8.7会员整合通行证发布
·[官方插件]帝国CMS-访问统计插件
·[官方插件]帝国CMS-sitemap插件
·[官方插件]帝国CMS内容页评论AJAX分
   类别最新
·谈谈JDBC
·JDBC专题介绍
·JDBC接口技术
·利用weblogic的POOL(连接池)连接
·Jsp中调用Oracle存储过程的小例子
·JSP数据库操作例程
·JSP数据库连接大全
·用连接池提高Servlet访问数据库的效
·一种简单JDBC连接池的实现
·数据库连接池Java实现小结
 
关于帝国 | 广告服务 | 联系我们 | 程序开发 | 网站地图 | 留言板 帝国网站管理系统