会话bean可以分为有状态会话bean(stateful bean)和无状态会话bean(stateless bean),有状态会话bean可以在客户访问之间保存数据,而无状态会话bean不会在客户访问之间保存数据。两者都实现了javax.ejb.sessionbean接口,ejb容器区通过部署文件ejb-jar.xml来判断是否为一个sessionbean提供保存状态的服务,另外,在程序实现上,无状态bean不能声明实例变量,每个方法只能操作方法传来的参数。实际上,我们第一节中的第一个ejb程序就是一个无状态session bean。
在本节中你将了解到:
什么是无状态session bean?
无状态session bean寿命周期
编写一个无状态session bean程序
部署到应用服务器
开发和部署测试程序
运行测试程序
什么是无状态session bean?
无状态session bean每次调用只对客户提供业务逻辑,但不保存客户端的任何数据状态。但并不意味着stateless类型的bean没有状态,而是这些状态被保持在客户端,容器不负责管理。如《再别康桥》中写到的"悄悄的我走了,正如我悄悄的来。挥一挥衣袖,不带走一片云彩"。
无状态session bean在ejb中是最简单的一种bean,如果数据实际上是数据的瞬像,则建议使用无状态会话bean。但是无状态会话bean也有自己的问题,本该存储在服务器端(j2ee服务器)的数据被存储在客户中,每次调用这些数据都要以参数的方式传递给bean,如果是一个比较复杂的数据集合,则网络需要传递大量数据,造成更多的负载。在客户端维护状态还要注意安全性问题,如果数据状态非常敏感,则不要使用无状态会话bean,这些情况可以使用状态会话bean,将用户状态保存到服务器中。
无状态session bean寿命周期
无状态session bean寿命周期由容器控制,bean的客户并不实际拥有bean的直接引用,当我们部署一个ejb时,容器会为这个bean分配几个实例到组件池(component pooling)中,当客户请求一个bean时,j2ee服务器将一个预先被实例化的bean分配出去,在客户的一次会话里,可以只引用一次bean,就可以执行这个bean的多个方法。如果又有客户请求同样一个bean,容器检查池中空闲的bean(不在方法中或事务中,如果一个客户长时间引用一个bean但执行一个方法后需要等待一段时间再执行另一个方法,则这段时间也是空闲的),如果全部的实例都已用完则会自动生成一个新的实例放到池中,并分配给请求者。当负载减少时,池会自动管理bean实例的数量,将多余的实例从池中释放。 无状态session bean有两种状态:存在或不存在。

<图2-1>
当客户端不存在一个无状态session bean时,通过远程主接口的create()方法创建一个bean,newinstance()负责将bean实例化,ejb容器调用bean类的setsessioncontext()方法把运行环境对象sessioncontext传递给bean;随后调用bean的ejbcreate方法进行必要的初始化和资源分配。在下面这个实战例子中,bean的实现类就是statelessdateejb类。
编写一个无状态的session bean程序
这个session bean组件提供一个日期计算器,通过getdayinrange()方法计算两个日期之间相差的天数,通过getdayforolympic()得到距离北京申办2008年奥林匹克运动会天数。并且我们为这个bean起名为statelessdate
设计一个无状态的session bean至少包括四个步骤:
开发主接口
开发组件接口
开发bean实现类
编写部署文件
注意:本节假设你使用的windows操作系统。如果使用其他操作系统,可能影响到存储路径和jdk命令,但这与程序代码和部署文件内容无关。
1.开发主接口(statelessdatehome.java):
是由bean开发人员编写的一个bean的主接口(interface)程序,负责控制一个bean的生命周期(生成、删除、查找bean)。只需要开发人员给出一个主接口类,类方法的实现由容器来完成。主接口扩展了javax.ejb.ejbhome接口,参考avax.ejb.ejbhome接口定义如下:
package javax.ejb;
import java.rmi.remote;
import java.rmi.remoteexception;
public interface ejbhome extends remote{
public abstract ejbmetadata getejbmetadata() throws remoteexception;
public abstract homehandle gethomehandle() throws remoteexception;
public abstract void remove(object obj) throws remoteexception,removeexception;
public abstract void remove(handle handle) throws remoteexception,removeexception;
}
方法getejbmetadata()返回ejbmetadata接口的引用,取得bean的信息,ejbmetadata不是远程接口。这个类扩展了java.io.serializable,所以可序列化,具有序列化的特性
方法gethomehandle()返回主对象的句柄,句柄是主接口statelessdatehome的持久性引用,这个类扩展了java.io.serializable,所以可序列化,具有序列化的特性,homehandle对象可以传递给另一个jvm,且不传递安全信息,这样新的应用可以不使用jndi来查找对象既可以获得这个主接口,并来创建和获得bean实例。
方法remove()用来删除一个bean的实例,对于一个会话bean,执行remove操作将引用的bean返回到池中,由池来管理其生命周期。
一般情况下,习惯将主接口的命名规则规定为
大部分逻辑方法已经被ejbhome定义,在我们要设计的远程主接口statelessdatehome里,不必再重新定义。值得注意的是,我们需要为这个接口定义一个create()方法,用来获得一个实例bean的引用,返回的对象类型是组件接口类statelessdate。
statelessdatehome.java代码:
import java.rmi.remoteexception;
import javax.ejb.createexception;
import javax.ejb.ejbhome;
public interface statelessdatehome extends ejbhome{
public statelessdate create() throws remoteexception,createexception;
}
假设我们保存到d:/ejb/statelessdate/src/statelessdatehome
.java
2.开发组件接口(statelessdate.java):
当远程用户调用主接口类生成方法(create())时,客户要得到一个组件的远程引用,因此ejb容器要求你为这个bean的所有方法提供一个接口类,而类的实现则与远程主接口statelessdatehome一样由容器在部署时自动生成。
组件接口扩展了avax.ejb.ejbobject接口,参考avax.ejb.ejbobject接口定义如下:
package javax.ejb;
import java.rmi.remote;
import java.rmi.remoteexception;
public interface ejbobject extends remote{
public abstract ejbhome getejbhome() throws remoteexception;
public abstract handle gethandle() throws remoteexception;
public abstract object getprimarykey() throws remoteexception;
public abstract boolean isidentical(ejbobject ejbobject) throws remoteexception;
public abstract void remove() throws remoteexception,removeexception;
}
方法getejbhome()返回远程主接口对象的引用
方法gethandle() 当前组件接口对象的句柄,和远程主接口的句柄homehandle一样,这个对象是被序列化的,所以可以保存到本地或通过rmi/iiop协议传输给其他jvm上的客户使用,而免去jndi查找和调用主接口的create方法,只要执行handle.getejbobject()方法即可取得这个bean实例的引用。
getprimarykey()方法一般用于entity bean,如果在session bean中调用,抛出java.rmi.remoteexception。
方法isidentical()用于对当前引用的bean实例和另一bean实例进行比较,因为即便是bean实例相同但有可能不是来自同一个引用,不能使用equals()方法。
方法remove() 删除当前引用的bean实例,由容器来决定是否真的释放气内存,通常会返换到组件池中。注意删除之后要将对象的引用指向为null。
一般情况下,习惯将组件接口的命名规则规定为
大部分逻辑方法已经被ejbobject 定义,在我们要设计的组件接口statelessdate里,不必再重新定义,只要我们重申组件中有关业务逻辑的接口即可。逻辑方法getdayinrange()得到两个日期间的天数间隔,如果输入的时间非法或不合适将抛出insufficientdateexception异常。逻辑方法getdayforolympic()得到距离北京申办奥运会的天数,如果输入的时间非法或不合适将抛出insufficientdateexception异常。
statelessdate.java代码:
import javax.ejb.ejbobject;
import java.rmi.remoteexception;
import java.util.date;
public interface statelessdate extends ejbobject{
public int getdayinrange(date lowerlimitdate,date upperlimitdate)
throws remoteexception,insufficientdateexception;
public int getdayforolympic()
throws remoteexception,insufficientdateexception;
}
假设我们保存到d:/ejb/st
闽公网安备 35060202000074号