服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

如何开发会话bean(有状态会话bean)


  会话bean可以分为有状态会话bean(stateful bean)和无状态会话bean(stateless bean),有状态会话bean可以在客户访问之间保存数据,而无状态会话bean不会在客户访问之间保存数据。两者都实现了javax.ejb.sessionbean接口,ejb容器区通过部署文件ejb-jar.xml来判断是否为一个sessionbean提供保存状态的服务,另外,在程序实现上,无状态bean不能声明实例变量,每个方法只能操作方法传来的参数,如果需要在引用期间维持一些数据状态,以在其他方法中可以引用,则可以把bean设计成有状态会话bean。在第二节中我们用一个satatelessdate bean例子描述了开发无状态会话bean的过程及特性,下面的一节将介绍关于有状态会话bean的一些特性和寿命周期,并用一个例子来证明这些特性。
  
  在本节中你将了解到:
  
  什么是有状态session bean?
  
  有状态session bean寿命周期
  
  编写一个有状态session bean程序
  
  部署到应用服务器
  
  开发和部署测试程序
  
  运行测试程序
  
  什么是有状态session bean?
  有状态会话bean(stateful session bean)就是在客户引用期间维护bean中的所有实例数据的状态值,这些数据在引用期间可以被其他方法所引用,其他客户不会共享同一个session bean的实例。bean的状态被保存到临时存储体中,因为bean是可以被序列化的,所以同样也可以把一个bean状态保存到文件系统或数据库中。因为在调用方法时需要维护状态(这部分是有开销的),所以只有需要维护客户状态时才使用有状态会话bean。典型的会话bean是购物车,当一个客户第一次打开购物车时,系统为他分配一个购物车的会话bean,在以后,每当客户选购了商品将改变购物车的商品记录,而这些记录数据将保存到用户会话数据中。
  
  有状态session bean寿命周期
  有状态session bean寿命周期由容器控制,bean的客户并不实际拥有bean的直接引用,当我们部署一个ejb时,容器会为这个bean分配几个实例到组件池(component pooling)中,当客户请求一个bean时,j2ee服务器将一个预先被实例化的bean分配出去,在客户的一次会话里,可以只引用一次bean,就可以执行这个bean的多个方法。如果又有客户请求同样一个bean,容器检查池中空闲的bean(不在方法中或事务中,如果一个客户长时间引用一个bean但执行一个方法后需要等待一段时间再执行另一个方法,则这段时间也是空闲的),如果全部的实例都已用完则会自动生成一个新的实例放到池中,并分配给请求者。当负载减少时,池会自动管理bean实例的数量,将多余的实例从池中释放。
  
  有状态会话bean的寿命周期比无状态会话bean更加的复杂,有状态会话bean有四种状态:
  
  不存在
  
  方法现成
  
  事务中方法现成
  
  钝化
  
  如图3-1所示:
  
 如何开发会话bean(有状态会话bean)

  <图3-1>
  
  有状态会话bean的初始化状态为不存在,当有客户引用一个bean时,按照顺序调用newinstance()、setsessioncontext()和ejbcreate()方法,与第一节中讲到的无状态调用顺序相同。当处于方法现成状态时,如果客户调用remove()方法,则回到不存在状态,并触发bean的ejbremove()方法。如果客户长时间不调用bean或服务器准备释放一些内存资源,则容器将这些bean从组件池中钝化,钝化过程容器将调用bean的ejbpassivate()方法,使程序员有机会在钝化bean时释放分配的资源。当一个客户请求一个被钝化的bean时,容器可以激活bean,激活过程容器将调用ejbactivate()放,使程序员有机会在bean转到方法现成状态时分配bean所需的资源。
  
  bean本身可以管理事务(bmt bean-managed transactions),也可以由容器管理事务(cmt container-managed transation)。对于cmt,容器在方法开始时打开事务,在方法结束时实现事务。bean开发人员可以通过afterbegin()、beforecompletion()、aftercompletion(boolean)来获取事务的各个状态,如果aftercompletion(boolean)中boolean变量为true表示事务完成,为false表示事务被撤消。
  
  编写一个有状态session bean程序
  假设这次我们要为一个基金组织编写一个基金帐户的bean组件,这个组件将为基金管理系统提供一个基金帐户的基本功能。为了能够描述清楚有状态会话bean的特性,我们将之简化成提供三个业务逻辑接口:addfunds()方法为一个基金帐户添加基金,removefunds()方法从基金帐户中取出基金,方法getbalance()为我们提供一个基金帐户的余额查询。我们为这个bean起名为statefulaccount
  
  设计一个有状态的session bean至少包括四个步骤:
  
  开发主接口
  
  开发组件接口
  
  开发bean实现类
  
  编写部署文件
  注意:本节假设你使用的windows操作系统。如果使用其他操作系统,可能影响到存储路径和jdk命令,但这与程序代码和部署文件内容无关。
  
  1.开发主接口(statefulaccounthome.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()返回主对象的句柄,句柄是主接口statelessaccounthome的持久性引用,这个类扩展了java.io.serializable,所以可序列化,具有序列化的特性,homehandle 对象可以传递给另一个jvm,且不传递安全信息,这样新的应用可以不使用jndi来查找对象既可以获得这个主接口,并来创建和获得bean实例。
  
  方法remove()用来删除一个bean的实例,对于一个会话bean,执行remove操作将引用的bean返回到池中,由池来管理其生命周期。
  
  一般情况下,习惯将主接口的命名规则规定为<bean-name>home,所以我们把这个主接口类起名为statefulaccounthome
  
  大部分逻辑方法已经被ejbhome定义,在我们要设计的远程主接口中,不必再重新定义。值得注意的是,我们需要为这个接口定义一个create()方法,用来获得一个实例bean的引用,返回的对象类型是组件接口类statefulaccount。与第二节的statelessdatehome类定义基本相同,不同的是create()方法需要一个double类型的fund参数,当客户创建一个bean引用时,我们将通过这个参数初始化基金帐户的余额。fund数值的状态将由容器来维护。
  
  statefulaccounthome.java代码:
  
  import java.rmi.remoteexception;
  import javax.ejb.createexception;
  import javax.ejb.ejbhome;
  
  public interface statefulaccounthome extends ejbhome{
   public statefulaccount create(double fund) throws remoteexception,createexception;
  }
  
  假设我们保存到d:/ejb/statefulaccount/src/statefulaccounthome .java
  
  2.开发组件接口(statefulaccount.java):
  
  当远程用户调用主接口类生成方法(create(double))时,客户要得到一个组件的远程引用,因此ejb容器要求你为这个bean的所有方法提供一个接口类,而类的实现则与远程主接口statefulaccounthome 一样由容器在部署时自动生成。
  
  组件接口扩展了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实例进行比较

扫描关注微信公众号