EJB学习笔记五(SessionBean的生命周期)
1.前言前几篇主要介绍了有状态和无状态、远程和本地调用的SessionBean,这篇介绍一下有关SessionBean的生命周期。SessionBean处于EJB容器管理之下,对于客户端来说是完全透明的。但对于开发者来说,必须要了解SessionBean在EJB容器中的运行规律。
2.为何会有SessionBean的生命周期
EJB容器创建和管理SessionBean实例,有些时候,可能也需要定制SessionBean的管理过程。例如,我想在创建SessionBean实例的时候初始化字段变量,或者在bean实例被销毁的时候关掉外部资源。这些都可以通过在bean类中定义生命周期方法,采用回调来实现。
3.无状态SessionBean的生命周期
分析:通过上图可以看出,如果需要改变无状态的SessionBean的生命周期,可以通过使用注解的形式,例如@PostConstruct、@PreDestroy来实现
4.有状态的SessionBean
有状态的SessionBean声明周期就有点复杂,因为客户端需要维护状态。
分析:从图上可以看出,有状态的SessionBean比无状态的SessionBean多了一个生命周期,去活状态,因而这就多出了两个注解:@PrePassivate和@PostActivate
5.生命周期分析
@PostConstruct:当 bean 对象完成实例化后,使用了这个注释的方法会被立即调用。这个注释同时适用于
有状态和无状态的会话 bean。
·@PreDestroy:使用这个注释的方法会在容器从它的对象池中销毁一个无用的或者过期的 bean 实例之前调
用。这个注释同时适用于有状态和无状态的会话 bean。
·@PrePassivate:当一个有状态的 session bean 实例空闲过长的时间,容器将会钝化(passivate)它,并把它的状态保存在缓存当中。使用这个注释的方法会在容器钝化 bean 实例之前调用。这个注释适用于有状态的会话 bean。当钝化后,又经过一段时间该 bean 仍然没有被操作,容器将会把它从存储介质中删除。以后,任何针对该 bean方法的调用容器都会抛出例外。
·@PostActivate:当客户端再次使用已经被钝化的有状态 session bean 时,新的实例被创建,状态被恢复。使用此注释的 session bean 会在 bean 的激活完成时调用。这个注释只适用于有状态的会话 bean。
·@Init:这个注释指定了有状态 session bean 初始化的方法。它区别于@PostConstruct 注释在于:多个@Init注释方法可以同时存在于有状态 session bean 中,但每个 bean 实例只会有一个@Init 注释的方法会被调用。这取决于 bean 是如何创建的(细节请看 EJB 3.0 规范)
@PostConstruct :在@Init 之后被调用。
@Remove,特别是对于有状态 session bean。当应用通过存根对象调用使用了@Remove 注释的方法时,容器就知道在该方法执行完毕后,要把 bean 实例从对象池中移走。
6.Demo分析
由于远程调用的SessionBean的生命周期比较复杂,下面也就以远程调用生命周期为例,来展示一下。
生命周期方法
- <span style="font-family:SimSun;font-size:18px;">package ejbsessionbean;
- import javax.annotation.PostConstruct;
- import javax.annotation.PreDestroy;
- import javax.ejb.*;
- @Stateful
- @Remote(LifeCycle.class)
- public class LifeCycleBean implements LifeCycle {
- public LifeCycleBean(){
- System.out.println("构造函数初始化");
- }
- public String Say() {
- try {
- Thread.sleep(1000*10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return "这是会话Bean生命周期应用例子";
- }
- @Init
- public void initialize () {
- System.out.println("@Init事件触发");
- }
- @PostConstruct
- public void Construct () {
- System.out.println("@PostConstruct事件触发");
- }
- @PreDestroy
- public void exit () {
- System.out.println("@PreDestroy事件触发");
- }
- @PrePassivate
- public void serialize () {
- System.out.println("@PrePassivate事件触发");
- }
- @PostActivate
- public void activate () {
- System.out.println("@PostActivate事件触发");
- }
- @Remove
- public void stopSession () {
- System.out.println("@Remove事件触发");
- //调用该方法以通知容器,移除该bean实例、终止会话。方法体可以是空的。
- }
- }
- </span>
测试方法
- <span style="font-family:SimSun;font-size:18px;">package com.test;
- import javax.naming.InitialContext;
- import javax.naming.NamingException;
- import ejbsessionbean.LifeCycle;
- public class testDemo {
- public static void main(String[] args) throws NamingException {
- InitialContext ctx=new InitialContext();
- LifeCycle lifecycle = (LifeCycle) ctx.lookup("LifeCycleBean/remote");
- System.out.println(lifecycle.Say());
- lifecycle.stopSession();
- System.out.println("请注意观察Jboss控制台输出.等待10分钟,容器将会钝化此会话Bean,@PrePassivate注释的方法将会执行");
- System.out.println("你可以调用stopSession方法把会话Bean实例删除。在删除会话Bean时,将触发@PreDestroy事件");
- }
- }
- </span>
经过测试,会在创建EJB对象实例的时候,自动的回调各个生命周期方法。
7.小结
SessionBean的生命周期方法的方法名可以是任意的
SessionBean的生命周期方法不能定义任何形参
SessionBean的生命周期方法的返回值必须是void
SessionBean的生命周期方法不能声明抛出checked异常
SessionBean中相同类型的生命周期方法最多只能定义一个,比如@PostConstruct修饰的方法最多只能有一个。
============================================================
1.前言
在EJB概述的博客中也曾提到过EJB3.0中的SessionBean的两种状态,下面呢,通过本篇博客深入的分析一下,两种状态的SessionBean到底有何区别。
2.开发一个有状态的SessionBean
2.1、接口
- <span style="font-family:SimSun;font-size:18px;"><span style="font-family:SimSun;font-size:18px;">package com.test;
- public interface StateFulEjbBean {
- public void compute(int i);
- public int getResult();
- }
- </span></span>
2.2、实现类
- <span style="font-family:SimSun;font-size:18px;"><span style="font-family:SimSun;font-size:18px;">package com.test;
- import javax.ejb.Remote;
- import javax.ejb.Stateful;
- //有状态的SessionBean实现类
- @Stateful
- //远程访问接口
- @Remote
- public class StateFulEjbBeanImp implements StateFulEjbBean {
- private int state;
- public void compute(int i) {
- state=state+i;
- }
- public int getResult() {
- return state;
- }
- }
- </span></span>
3.开发一个无状态的SessionBean
有状态和无状态SessionBean,在定义上,只不过是注解形式不一样而已,有状态的注解关键字是StateFul,无状态是StateLess
- <span style="font-family:SimSun;font-size:18px;"><span style="font-family:SimSun;font-size:18px;">package com.test;
- import javax.ejb.Remote;
- import javax.ejb.Stateless;
- @Stateless
- @Remote
- public class StateLessEjbBeanImpl implements StateLessEjbBean{
- private int state;
- public void compute(int i) {
- state=state+i;
- }
- public int getResult() {
- return state;
- }
- }
- </span></span>
4.简单测试
4.1、有状态客户端编写
下面通过一个客户端来分别调用一下,上述Demo中的有状态和无状态的SessionBean,调用方法和上一篇博客类似,就不再详细的介绍。
- <span style="font-family:SimSun;font-size:18px;"><span style="font-family:SimSun;font-size:18px;">package com.test;
- import javax.naming.InitialContext;
- import javax.naming.NamingException;
- public class StatefulEjbClient {
- public static void main(String[] args) throws NamingException {
- //如果不配置JNDI的话,需要在代码中书写如下
- // Properties props=new Properties();
- // 第一次会话
- InitialContext context = new InitialContext();
- StateFulEjbBean ejb1 = (StateFulEjbBean) context
- .lookup("StateFulEjbBeanImp/remote");
- System.out.println(ejb1.getResult());
- ejb1.compute(1);
- System.out.println(ejb1.getResult());
- //第二次会话,新的对象,新的会话,因为是有状态的,所以会创建新的对象,变量不是单例的
- StateFulEjbBean ejb2 = (StateFulEjbBean) context
- .lookup("StateFulEjbBeanImp/remote");
- System.out.println(ejb2.getResult());
- ejb2.compute(1);
- System.out.println(ejb2.getResult());
- ejb2.compute(1);
- }
- }
- </span></span>
4.2、测试有状态SessionBean
当把EJB部署到JBoss中,测试上述实例的时候,无论测试几次,结果都如下所示
4.3、测试无状态SessionBean
当把EJB项目部署到JBoss中,测试上述实例的时候,每次运行的结果不一样,有一个累加的过程,如下图
4.4.两种状态的区别
1.关键字上一个是StateFul。另一个是StateLess
2.无状态的SessionBean有以下好处
EJB容器无须为每个客户端分配一个EJB实例(对于有状态的Session Bean而言,系统必须为每个客户端分配一个EJB实例),可以减少创建、维护EJB实例的系统开销
EJB容器无须维护EJB的内部状态,系统开销大大降低。
3.有状态的SessionBean使用场景
客户端数量比较有限 ,当客户端数量巨大时,那就意味着EJB容器要同时为何大量有状态的EJB,这将导致EJB容器的性能直线下降。因此,只有当客户端数量比较固定时,才可以考虑使用有状态的SessionBean
客户端与服务端之间的会话比较多。
================================================================================
一。Enterprise Beans在EJB(Enterprise Java Beans)中定义了两种不同类别的Enterprise Bean :*会话 Bean (Session Bean)
*实体Bean (Entity Bean)
1. 会话 Bean (Session Bean)
会话 Bean 是调用它的客户端代码要完成的工作。当客户端与服务器建立联系,那么一个会话 Bean 就建立起来了。根据会话 Bean 的状态不同有分为:A. 状态会话 Bean (Stateful Session Bean)
B. 无状态会话 Bean (Stateless Session Bean)
1.1 状态会话 Bean (Stateful Session Bean)
当客户机和服务器建立连接之后,状态会话 Bean (Stateful Session Bean) 将一直在客户机和服务器之间保持着用户的某个状态。例如:用户使用银行的ATM时,经过验证之后,用户可以连续执行多次操作,在这个过程当中,用户的合法状态将一直被保留,直到她将信用卡取出,结束这次操作。这时,状态会话 Bean (Stateful Session Bean) 也就被销毁。
1.2无状态会话 Bean (Stateless Session Bean)
当客户机和服务器建立连接之后,无状态会话 Bean (Stateless Session Bean)处理单一的用户请求或商务过程。无状态会话 Bean (Stateless Session Bean)不需要从以前的请求中提取任何状态。例如,用户的用户密码确认。用户输入密码后,发送请求。组件返回真或假来确认用户,一旦过程完成,无状态会话 Bean (Stateless Session Bean) 也宣告结束。
2. 实体Bean (Entity Bean)
实体Bean (Entity Bean)只是数据模型,它不包括商务逻辑。实体Bean (Entity Bean)可以将关系/对象数据库的数据映射到内存中供其它组件使用。实体Bean (Entity Bean)是一直存在的,而且具有很高的容错性能。实体Bean (Entity Bean)能供允许多用户同时访问。
二。 会话 Bean (Session Bean)
Ejb的执行过程是被放在一个EJB容器中进行的,所以客户端不会直接调用我们写好的Enterprise Bean ,而是调用EJB容器生成的一个EJBObject (EJB对象)来实现。那么,我们在编写服务器端的Enterprise Bean 时,就要考虑这点。既然客户端不能直接访问,就由EJBObject来代劳,所以在编写服务器端时,就要编写服务器端的一个接口(Remote)用来与客户机联系,实力化EJBObject.要生成EJBObject 就要调有Home 接口,来建立这个实力。
图一
以下是会话 Bean 的代码分析:
import javax.ejb.*;
public class sailorsy implements SessionBean{
private SessionContext ctx=null;
public voic setSessionContext(SessionContext ctx){
this.ctx=ctx;
}//setSessionContext
public void ejbCreate() {
}//ejbCreate
public void ejbPassivate() {
}//ejbPassivate
public void ejbActivate() {
}//ejbActivate
}//class sailorsy
2.ejbCreate(…)方法
它可以初始化Enterprise Bean ,可以定义不同的ejbCreate(…)方法,每个方法所带的参数不同。但是,必许要存在至少一种。
3.ejbPassivate()方法
如果初始化的Enterprise Bean 过多,EJB容器将其中的一些挂起(passivate),释放他们所占用的空间。
4.ejbActivate()方法
和ejbPassivate正好相反,它将被挂起的Bean从新调回。
5.ejbRemove()方法
它可以清除EJB容器中的Bean。
以上这些是EJB必需的回调方法,我们可以在里面加入自己的方法,加入自己的商务逻辑。
B.Home 接口: sailorsyHome
import java.rmi.*;
import javax.ejb.*;
public interface sailorsyHome extends EJBHome {
public sailorsyRemote create() throws RemoteException, CreateException;
}
C. Remote接口:sailorsyRemote
import java.rmi.*;
import javax.ejb.*;
public interface sailorsyRemote extends EJBObject {
public java.lang.String showname() throws RemoteException;
}
三.调用会话 Bean:sailorsyTestClient1
import javax.naming.*;
import javax.ejb.*;
import javax.rmi.PortableRemoteObject;
import java.rmi.*;
public class sailorsyTestClient1 {
private sailorsyHome sailorsyHomeObject = null;
//Construct the EJB test client
public sailorsyTestClient1() {
try {
//以下是客户端使用JNDI定位Home对象。
Context ctx = new InitialContext();
//look up jndi name
Object ref = ctx.lookup("sailorsy");
//cast to Home interface
sailorsyHomeObject = (sailorsyHome) PortableRemoteObject.narrow(ref, sailorsyHome.class);
}
catch(Exception e) {
e.printStackTrace();
}
}
//----------------------------------------------------------------------------
// Utility Methods
//----------------------------------------------------------------------------
public sailorsyHome getHome() {
return sailorsyHomeObject;
}
//Main method
public static void main(String[] args) throws Exception{
sailorsyTestClient1 client = new sailorsyTestClient1();
sailorsyRemote sr=client.getHome() .create() ;
String s=sr.showname() ;
system.out.print(s);
// Use the getHome() method of the client object to call Home interface
// methods that will return a Remote interface reference. Then
// use that Remote interface reference to access the EJB.
}
}
以上的EJB在win2000+jbuilder5/jbuilder6+BAS4.5经过测试。
================================================================
EJB学习笔记三(有状态和无状态SessionBean的区别)
1.前言
在EJB概述的博客中也曾提到过EJB3.0中的SessionBean的两种状态,下面呢,通过本篇博客深入的分析一下,两种状态的SessionBean到底有何区别。
2.开发一个有状态的SessionBean
2.1、接口
- <span style="font-family:SimSun;font-size:18px;"><span style="font-family:SimSun;font-size:18px;">package com.test;
- public interface StateFulEjbBean {
- public void compute(int i);
- public int getResult();
- }
- </span></span>
2.2、实现类
- <span style="font-family:SimSun;font-size:18px;"><span style="font-family:SimSun;font-size:18px;">package com.test;
- import javax.ejb.Remote;
- import javax.ejb.Stateful;
- //有状态的SessionBean实现类
- @Stateful
- //远程访问接口
- @Remote
- public class StateFulEjbBeanImp implements StateFulEjbBean {
- private int state;
- public void compute(int i) {
- state=state+i;
- }
- public int getResult() {
- return state;
- }
- }
- </span></span>
3.开发一个无状态的SessionBean
有状态和无状态SessionBean,在定义上,只不过是注解形式不一样而已,有状态的注解关键字是StateFul,无状态是StateLess
- <span style="font-family:SimSun;font-size:18px;"><span style="font-family:SimSun;font-size:18px;">package com.test;
- import javax.ejb.Remote;
- import javax.ejb.Stateless;
- @Stateless
- @Remote
- public class StateLessEjbBeanImpl implements StateLessEjbBean{
- private int state;
- public void compute(int i) {
- state=state+i;
- }
- public int getResult() {
- return state;
- }
- }
- </span></span>
4.简单测试
4.1、有状态客户端编写
下面通过一个客户端来分别调用一下,上述Demo中的有状态和无状态的SessionBean,调用方法和上一篇博客类似,就不再详细的介绍。
- <span style="font-family:SimSun;font-size:18px;"><span style="font-family:SimSun;font-size:18px;">package com.test;
- import javax.naming.InitialContext;
- import javax.naming.NamingException;
- public class StatefulEjbClient {
- public static void main(String[] args) throws NamingException {
- //如果不配置JNDI的话,需要在代码中书写如下
- // Properties props=new Properties();
- // 第一次会话
- InitialContext context = new InitialContext();
- StateFulEjbBean ejb1 = (StateFulEjbBean) context
- .lookup("StateFulEjbBeanImp/remote");
- System.out.println(ejb1.getResult());
- ejb1.compute(1);
- System.out.println(ejb1.getResult());
- //第二次会话,新的对象,新的会话,因为是有状态的,所以会创建新的对象,变量不是单例的
- StateFulEjbBean ejb2 = (StateFulEjbBean) context
- .lookup("StateFulEjbBeanImp/remote");
- System.out.println(ejb2.getResult());
- ejb2.compute(1);
- System.out.println(ejb2.getResult());
- ejb2.compute(1);
- }
- }
- </span></span>
4.2、测试有状态SessionBean
当把EJB部署到JBoss中,测试上述实例的时候,无论测试几次,结果都如下所示
4.3、测试无状态SessionBean
当把EJB项目部署到JBoss中,测试上述实例的时候,每次运行的结果不一样,有一个累加的过程,如下图
4.4.两种状态的区别
1.关键字上一个是StateFul。另一个是StateLess
2.无状态的SessionBean有以下好处
EJB容器无须为每个客户端分配一个EJB实例(对于有状态的Session Bean而言,系统必须为每个客户端分配一个EJB实例),可以减少创建、维护EJB实例的系统开销
EJB容器无须维护EJB的内部状态,系统开销大大降低。
3.有状态的SessionBean使用场景
客户端数量比较有限 ,当客户端数量巨大时,那就意味着EJB容器要同时为何大量有状态的EJB,这将导致EJB容器的性能直线下降。因此,只有当客户端数量比较固定时,才可以考虑使用有状态的SessionBean
客户端与服务端之间的会话比较多。
========================================================================
EJB学习: Stateful SessionBean
二、Stateful
有状态会话Bean与无状态会话Bean在代码上没有任何区别。
作为一个EJB必需有三个类组成:
1、home接口
2、远程接口
3、实现类
为什么要这样设计呢?这是由EJB的功能决定的,因为EJB的设计初衷是为了分布式计算,也就是在不同的JVC中运行,而又不能曝露自己的实现类,只能曝露home接口和远程接口给客户端:
home接口:
RnbEjbHome.java:
import javax.ejb.EJBHome;
import javax.ejb.CreateException;
import java.rmi.RemoteException;
public interface RnbEjbHome extends EJBHome {//目的是产生远程接口
}
远程接口:
RnbEjb.java:
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface RnbEjb extends EJBObject {
//定义了一个与实现类中一样的方法,客户最后也就是调用此方法
}
实现类:
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.ejb.CreateException;
//有状态会话bean
//多个客户端调用服务器的ejb时,实际上,服务器端只生成一个ejb对 象为其服务,
//顺序是:构造函数-》setSessionContext-》ejbCreate(),目的是节省内存
//客户端可以命令服务端ejb消亡,
//在有状态Bean下,可以消亡其ejb
//客户端在调用create()方法时就生成ejb对象,没有延迟加载
//特点:你可以用成员变量来定义一些状态信息,你可以用额外的ejbCreate()来初始化这些状态信息
//状态会话Bean其实也可以定义成员变量,但是这些成员变量必须是所有客户共享的如数据库连接,有状态会话Bean可以定义自己的变量
//缺点:消耗资源
//解决办法:服务器:1钝化机制,长期不用的EJB对象,被暂时保存在缓存中,腾出内存,自动调用ejbPassivate(),如果长期不用,服务器自动调用remove()
//2:客户端在使用时要将对应的ejb对象消亡,一般情况下是将远程接口保存在session中,在退出系统时,调用其remove()方法
//尽量不使用有状态的会话Bean,因为可以用Web中有session一来保存状态
public classRnbEjbBean implements SessionBean {
}
同样也有以下四个:
1、ejb-jar.xml(重要:EJB自身的信息)
2、weblogic-ejb-jar.xml(重要:在weblogic中的JNDI配置)
3、ejb-modeler-layout.xml(不太重要,一般不用修改)
4、weblogic-cmp-rdbms-jar.xml(实体Bean的CMP创建会用到)
ejb-jar.xml:这个配置文件就把home接口、远程(remote)接口、实现类绑定在一起了
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems,Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">
<ejb-jar>
</ejb-jar>
weblogic-ejb-jar.xml(重要:在weblogic中的JNDI配置)
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE weblogic-ejb-jar PUBLIC "-//BEA Systems,Inc.//DTD WebLogic 8.1.0 EJB//EN" "http://www.bea.com/servers/wls810/dtd/weblogic-ejb-jar.dtd">
<weblogic-ejb-jar>
</weblogic-ejb-jar>
客户端调用:
EjbFactory.java //目的得到home接口
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class EjbFactory {
}
Client1.java客户调用类
import prormb.RnbEjbHome;
import prormb.RnbEjb;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
public class Client1 {
}