===================================
提纲:
===================================
一、概述
二、无
状态会话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)