一起学习
胡德平 编译(hudeping@263.net )
会话组件在J2EE服务器中表现为一个客户端,该客户端通过调用会话组件方法获取远程服务。会话组件为其客户端完成指定任务,屏蔽了客户端需要在服务器端执行商业任务的复杂性。
正如其名所暗示,会话组件工作原理类似于交互会话。会话组件是不共享得--它在会话期间只能与一个客户端交互、一个用户交互。和交互会话一样,会话组件没有持续,当客户端中断会话后会话组将也将中断并不再与客户端相关联。
会话组件是非常有用而强大的,因为它将你的客户端范围扩展到了远程服务器--并且它们容易开发。本文将通过简单示例,为你展示如何编写、编译和部署一个简单的会话组件。
会话组件示例
本文所举例子是一个在线书店中关于购物车的演示,组件的客户端需要实现往购物车增加、删除、查询等操作功能。为了实现这个例子,你需要完成下面程序的编码:
·会话组件类(CartEJB.java)
·本地接口(CartHome.java)
·远程接口(Cart.java)
上述三个文件在所有企业组件(EJB)编程中都是必须的,为了满足特定应用更多需求,企业组件可能需要更多的帮助类(helper class)。CartEJB会话组件使用了两个帮助类,即 BookException.java和IdVerifier.java。
如果你安装了J2EE,上述源码文件可以在doc/guides/ejb/examples/cart目录中找到。
会话组件类
本例子中会话组件类命名为CartEJB.,与其它所有的会话组件一样, CartEJB 类必须满足以下要求:
·实现SessionBean 接口
·类定义为公共类(public)
·类不能是抽象类或终极类
·实现一个或多个ejbCreate方法
·实现商业方法
·有无参数的构造方法
·必须没有终极方法(finalize method)
CartEJB源码如下:
import java.util.*;
import javax.ejb.*;
public class CartEJB implements SessionBean {
String customerName;
String customerId;
Vector contents;
public void ejbCreate(String person) throws CreateException {
if (person == null) {
throw new CreateException("Null person not allowed.");
}
else {
customerName = person;
}
customerId = "0";
contents = new Vector();
}
public void ejbCreate(String person, String id)
throws CreateException {
if (person == null) {
throw new CreateException("Null person not allowed.");
}
else {
customerName = person;
}
IdVerifier idChecker = new IdVerifier();
if (idChecker.validate(id)) {
customerId = id;
}
else {
throw new CreateException("Invalid id: " id);
}
contents = new Vector();
}
public void addBook(String title) {
contents.addElement(title);
}
public void removeBook(String title) throws BookException {
boolean result = contents.removeElement(title);
if (result == false) {
throw new BookException(title " not in cart.");
}
}
public Vector getContents() {
return contents;
}
public CartEJB() {}
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext sc) {}
}
|
会话组件接口(SessionBean)
会话组件接口SessionBean继承EnterpriseBean 接口,而后者继承了Serializable 接口。SessionBean 接口定义了ejbRemove, ejbActivate, ejbPassivate 和setSessionContext 方法。CartEJB 类没有用到这些类,但必须实现它们否则应该定义成抽象类。因此,这些方法在CartEJB类中被声明为空方法,随后的内容中将就这些方法何时有什么用途进行说明。
EjbCreate方法
由于企业组件运行在EJB容器中,所以客户端无法直接示例化这些组件。只有EJB容器可以示例化企业组件,在本文所述例子中完成实例化需要实现以下步骤:
1. 客户端调用本地接口的create方法:
Cart shoppingCart = home.create("Duke DeEarl","123");
2. EJB容器实例化企业组件。
3. EJB容器调用CartEJB中对应的ejbCreate方法:
public void ejbCreate(String person, String id)
throws CreateException {
if (person == null) {
throw new CreateException("Null person not allowed.");
}
else {
customerName = person;
}
IdVerifier idChecker = new IdVerifier();
if (idChecker.validate(id)) {
customerId = id;
}
else {
throw new CreateException("Invalid id: " id);
}
contents = new Vector();
}
|
典型的, ejbCreate 创建方法初始化企业组件的状态。前面所提到的ejbCreate方法,如通过create方法传递参数初始化客户名( customerName )和客户ID( customerId )变量。
企业组件可以有一个或多个ejbCreate 方法,方法的命名必须满足下面需求:
·访问控制修饰词必须是public.
·返回值必须是void.
·参数必须是Java RMI的合法类型
·修饰词(modifier)不能是static或final.
此外,异常抛出必须包括javax.ejb.CreateException 和其它你应用中指定需要抛出的异常。如果输入的参数无效,ejbCreate 方法通常抛出CreateException 异常。
商业方法
会话组件的最根本的用法是实现客户端所需要的商业任务,客户端通过create方法返回的参考调用远程对象的商业方法。从客户端角度看,商业方法是在本地运行的,但实际上是在远程会话组件中运行,下面的代码摘录描述了CartClient程序中对商业方法的调用过程:
Cart shoppingCart = home.create("Duke DeEarl", "123");
. . .
shoppingCart.addBook("The Martian Chronicles");
shoppingCart.removeBook("Alice In Wonderland");
bookList = shoppingCart.getContents();
The CartEJB class implements the business methods in the following code:
public void addBook(String title) {
contents.addElement(new String(title));
}
public void removeBook(String title) throws BookException {
boolean result = contents.removeElement(title);
if (result == false) {
throw new BookException(title " not in cart.");
}
}
public Vector getContents() {
return contents;
}
|
商业逻辑的用法必须符合下面的规则:
·方法名不能与EJB体系结构规范中定义的有冲突,如不能命名为ejbCreate、ejbActive等
·访问控制修饰符必须是public
·参数类型必须是合法的Java RMI类型
·修饰词不能为static或final
方法抛出异常语句可能包括为具体应用而定义的异常,如当书不在购物车中时removeBook方法抛出BookException 异常。
为了指出如无法连接数据库这样的系统级错误,商业方法需要抛出javax.ejb.EJBException 异常。当商业方法抛出EJBException时,EJB容器将其包装成客户端捕获得RemoteException 异常。对于应用定义的异常BookException,容器是不会对其进行包装的,因为EJBException 是RuntimeException 异常的子类。
本地接口
本地接口继承EJBHome接口,主要用于定义客户端调度的create方法,如CartClient调用create方法:
Cart shoppingCart = home.create("Duke DeEarl", "123");
在本地接口中每个create 方法与组件类的一个ejbCreate 方法相对应,在CartEJB类中的ejbCreate方法的用法如下:
public void ejbCreate(String person) throws CreateException
. . .
public void ejbCreate(String person, String id)
throws CreateException
|
比较一下ejbCreate 与CartHome中create 方法的用法:
import java.io.Serializable;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface CartHome extends EJBHome {
Cart create(String person) throws RemoteException,
CreateException;
Cart create(String person, String id) throws RemoteException,
CreateException;
}
|
ejbCreate与 create 方法的用法类似,当在重要的方面是不一致的,本地接口中create方法的使用原则定义如下:
·create方法的参数数量和类型必须与所定义的ejbCreate 方法一致
·create方法参数和返回类型必须是合法的RMI类型
·create 方法企业组件远程接口类型(而djbCreate方法返回空)
·create方法异常抛出语句必须包括java.rmi.RemoteException和javax.ejb.CreateException
远程接口
远程接口继承于javax.ejb.EJBObject, 定义了客户端可以调用的商业方法,下面是购物车的远程接口代码 :
import java.util.*;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Cart extends EJBObject {
public void addBook(String title) throws RemoteException;
public void removeBook(String title) throws BookException,
RemoteException;
public Vector getContents() throws RemoteException;
}
|
远程接口定义方法必须遵循下述原则:
·每个在远程接口中定义的方法必须与企业组件类中多定义的方对应
·远程接口中方法的用法必须与企业组件类中的方法一致
·方法的参数和返回值必须是合法的RMI类型
·异常抛出语句必须包括java.rmi.RemoteException
帮助类
CartEJB 组件有两个帮助类: BookException和 IdVerifier,其中 BookException 是应用定义的异常而IdVerifier 用于在djbCreate方法中验证customerId 的有效性。帮助类在J2EE应用打包时包含在EJB.jar文件中,与企业组件类在同一包。
状态管理模式
在为会话组件指定不属描述符时,不想选择会话组件的状态模式:有状态和无状态。
有状态会话组件(Stateful Session Beans)
CartEJB 这个例子有三个实例变量:customerName, customerId, 和 contents,这些变量代表了购物车应用的会话状态。因为CartEJB 具有会话状态,所以把它叫做有状态会话组件。
状态在客户端与会话组件会话期间保留,当客户端移交(remove)组件、会话结束后状态消失。解决这种状态的短暂性不是什么问题,然而,因为客户端和组件之间会话结束后不再需要保留状态。
无状态会话组件(Stateless Session Beans)
无状态会话组件在会话期间不需要为特定的客户端保存状态,因而得名。当客户端调用无状态组件时,组件实例变量可能会有状态,但仅仅只是在调用过程中。当方法执行结束后,状态不再保留。除了方法调用期外,所有的无状态组件实例都是相等地,允许EJB容器将任何一个实例分配给任意一个苦户端。
因为无状态组件可以支持多个客户端,从拥有大量客户端的应用来客比有状态会话组件具有更大的灵活性。通常情况下,应用要求用更多的无状态组件而非有状态组件来相应相同的客户端。
有时,EJB容器会将状态组件写到缓存中,但无状态组件是从来不会这么做的。因此,相对而言无状态组件能够提供比有状态组件更高的性能。
无状态组件的本地接口必须有一个无参数的create 方法,同时会话组件也必须有个与其相对应的无参数ejbCreate 方法。
两种会话组件的选择
下列情况下,建议选择有状态组件:
·在组件创建时必须初始化其状态
·组件必须保留客户端方法商业方法的信息
·客户端是交互式应用
既然会话组件的主要目的是在J2EE服务器上表现客户端功能,更多的会话组件会是有状态会话组件。不过,在下述情况下建议使用无状态会话组件:
·组件执行的任务不讲究是否对应特定客户端,如数据库查询
·组件无需保留客户端对方法调用的信息
会话组件的生命周期
会话组件在其生命周期中有多个状态,其生命周期由EJB容器所管理而非应用本身。尽管应用无法管理组件的生命周期,但下面关于生命周期的信息对于管理如数据库连接的资源是有用的。
有状态会话组件的生命周期
有状态会话组件的生命周期源于客户端通过create 方法、EJB容器实例化组件后调用会话组件的setSessionContext 和ejbCreate 方法。此后,组件的商业方法处于可调用的就绪阶段。
在就绪阶段,EJB容器将判断组件是否可以处于非活动、钝化状态,然后将其从内存转移到缓存(典型的算法是采用最近最少调用算法),EJB容器调用组件的ejbPassivate 方法实现对组件的钝化。如果在钝化阶段有客户端调用该组件的商业方法,EJB容器将激活组件并将其恢复到就绪状态,然后调用组件的ejbActivate 方法。
在生命周期最后,客户端调用remove 方法、EJB容器调用组件的ejbRemove 方法,组件实例等待垃圾回收器回收处理。
在客户端编程中,主要通过调用两个生命周期相关方法-- create 和 remove 方法实现对生命周期的处理。下图中所有的方法调用都是由服务器端EJB容器实现的,如 ejbCreate 方法,允许你在组件实例化后进行数据库连接等操作。
图 有状态会话组件生命周期
五状态会话组件生命周期
因为无状态会话组件从来不被钝化,所有它的声明周期只有两个阶段:在商业方法调用过程中的不存阶段在和就绪阶段
下载本文示例代码
会话组件(Session Beans)会话组件(Session Beans)会话组件(Session Beans)会话组件(Session Beans)会话组件(Session Beans)会话组件(Session Beans)会话组件(Session Beans)会话组件(Session Beans)会话组件(Session Beans)会话组件(Session Beans)会话组件(Session Beans)会话组件(Session Beans)
阅读(107) | 评论(0) | 转发(0) |