EJB Session Bean有状态和无状态的区别与联系

刚开始对两种sessionbean存在误解,认为有状态是实例一直存在,保存每次调用后的状态,并对下一次调用起作用,而认为无状态是每次调用实例化一次,不保留用户信息。仔细分析并用实践检验后,会发现,事实恰好相反:有状态和无状态会话bean的本质区别是它们的生命期。
     
       首先解释一个下面要用到的概念--用户:sessionbean 的用户实际上就是直接调用ejb的类的实例,甚至是这个实例的某个方法。同一个类的不同实例对于session bean来说是不同的用户。

实例解析

有状态的StatefulEjb接口



package com.bjsxt.ejb;  
  
public interface StatefulEjb {  
    public void compute(int i);  
    public int getResult();  
  
}  



StatefulEjb的实现

package com.bjsxt.ejb;  
  
import javax.ejb.Remote;  
import javax.ejb.Stateful;  
@Stateful  
@Remote  
      
public class StatefulEjbBean implements StatefulEjb {  
  
    private int state;  
    public void compute(int i) {  
        state = state +1;  
  
    }  
  
      
    public int getResult() {  
          
        return state;  
    }  
  
}  




无状态接口StatelessEjb


package com.bjsxt.ejb;  
  
public interface StatelessEjb {  
    public void compute(int i);  
    public int getResult();  
  
}  



无状态接口StatelessEjb实现

package com.bjsxt.ejb;  
  
import javax.ejb.Remote;  
import javax.ejb.Stateless;  
  
@Stateless  
@Remote  
public class StatelessEjbBean implements StatelessEjb {  
  
    private int state;  
    public void compute(int i) {  
        state = state +1;  
  
    }  
  
      
    public int getResult() {  
          
        return state;  
    }  
  
}  



客户端配置jndi



java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory  
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces  
java.naming.provider.url=localhost


有状态的客户端编写

package com.bjsxt.ejb;  
  
import javax.naming.InitialContext;  
  
public class StatefulEjbClient {  
    public static void main(String[] args) throws Exception{  
        InitialContext context = new InitialContext();  
        //第一次回話  
        StatefulEjb ejb1 =(StatefulEjb)context.lookup("StatefulEjbBean/remote");  
        System.out.print(ejb1.getResult());  
        ejb1.compute(1);  
        System.out.print(ejb1.getResult());  
        ejb1.compute(1);  
        System.out.print(ejb1.getResult());  
        ejb1.compute(1);  
        System.out.print(ejb1.getResult());  
        ejb1.compute(1);  
        System.out.print(ejb1.getResult());  
        ejb1.compute(1);  
        System.out.print(ejb1.getResult());  
        ejb1.compute(1);  
        System.out.print(ejb1.getResult());  
    //第二次会话  
          
        StatefulEjb ejb2 = (StatefulEjb)context.lookup("StatefulEjbBean/remote");  
        System.out.print(ejb2.getResult());  
        ejb2.compute(1);  
        System.out.print(ejb2.getResult());  
        ejb2.compute(1);  
        System.out.print(ejb2.getResult());  
        ejb2.compute(1);  
        System.out.print(ejb2.getResult());  
        ejb2.compute(1);  
        System.out.print(ejb2.getResult());  
        ejb2.compute(1);  
        System.out.print(ejb2.getResult());  
        ejb2.compute(1);  
        System.out.print(ejb2.getResult());  
    }  
  
}  

结果:


无状态客户端编写

package com.bjsxt.ejb;  
  
import javax.naming.InitialContext;  
  
public class StatelessEjbClient {  
    public static void main(String[] args) throws Exception{  
        InitialContext context = new InitialContext();  
        //第一次回話  
        StatelessEjb ejb1 =(StatelessEjb)context.lookup("StatelessEjbBean/remote");  
        System.out.print(ejb1.getResult());  
        ejb1.compute(1);  
        System.out.print(ejb1.getResult());  
        ejb1.compute(1);  
        System.out.print(ejb1.getResult());  
        ejb1.compute(1);  
        System.out.print(ejb1.getResult());  
        ejb1.compute(1);  
        System.out.print(ejb1.getResult());  
        ejb1.compute(1);  
        System.out.print(ejb1.getResult());  
        ejb1.compute(1);  
          
    //第二次会话  
          
        StatelessEjb ejb2 = (StatelessEjb)context.lookup("StatelessEjbBean/remote");  
        System.out.print(ejb2.getResult());  
        ejb2.compute(1);  
        System.out.print(ejb2.getResult());  
        ejb2.compute(1);  
        System.out.print(ejb2.getResult());  
        ejb2.compute(1);  
        System.out.print(ejb2.getResult());  
        ejb2.compute(1);  
        System.out.print(ejb2.getResult());  
        ejb2.compute(1);  
        System.out.print(ejb2.getResult());  
        ejb2.compute(1);  
        System.out.print(ejb2.getResult());  
          
        System.out.println("ejb1 == ejb2 ?:"+(ejb1 == ejb2));  
          
    }  
  
}  


结果



       有状态会话bean:每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。
       无状态会话bean :bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。

    

   区别的根本原因

     
      对于有状态会话Bean来说,只要有客户端发送对有状态会话Bean的访问,服务器都会创建一个会话Bean实例与该客户端对应,这样这个实例与这个客户端就是一一对应的。如果客户端在Bean实例中保存了信息,之后还可以使用。
       对于无状态会话Bean来说,服务器端会维持一个实例池,创建好若干个实例对象供客户端调用。当从客户端发送创建会话Bean的请求时,并不一定会真的创建 EJB,多数情况下是从实例池中得到一个实例,用完之后重新放回实例池。如果下次再访问,再从实例池中取出一个实例使用,并不一定是上次的实例。即使两次访问使用的是同一个实例,在两次访问之间也有可能有其他的客户端访问了该实例。所以,并不能保证在多次访问之间的信息会被保存。所以,无状态会话Bean 不会专门保存客户端的信息。


   各自的优缺点
       
        因为有状态会话Bean需要保存特定客户端的信息,一个客户端对应一个实例,既是在当时客户端有连接没有访问的情况下,也要为这个客户端保留这个实例。这样随着客户端数量的增加,服务器端需要创建的实例的数量也在增加,增加到一定程度对服务器的性能就会有一定的影响。为了不对服务器的性能产生影响,通常服务器会进行一些优化。当客户端的数量超过某个值之后,就不创建新的实例。虽然不创建新的实例,还是需要对用户响应,这时候就采用共享实例的方式。会查看哪个实例虽然处于连接状态,但是没有访问,然后把这个实例的状态保存起来,使用这个实例为新的请求服务,对于原来的客户端来说,称为挂起。如果原来的客户端又发送请求了,会重新查找一个空闲的实例并且把已经保存好的状态恢复回来,这个过程称为激活。所以在有状态会话Bean的访问过程,经常会发生查找实例,激活挂起等操作,所以效率比较低。
       而发送对无状态会话Bean的请求的时候,可以随便取一个空闲的实例为客户端服务,所以效率比较高。
       有状态会话Bean的好处是,可以保存客户端的状态,所以客户端在后续访问的时候就可以少传递一些参数。而无状态会话Bean需要传递方法执行过程中需要的所有参数。


   如何选择
       根据上面分析的有状态会话Bean和无状态会话Bean的优缺点。如果要频繁的访问,并且多次访问之间会共享一些信息,这时候应该使用有状态会话Bean。对于不经常使用的功能,可以使用无状态会话Bean。无状态会话Bean的使用要比有状态会话Bean的使用多。



       项目中没有使用到有状态的会话bean,对于使用的相同数据,也是通过传递参数实现,并非设置到bean中的属性值中。对于无状态的会话bean,如果有属性,则可能影响到所有调用的用户,因为他是共享的。

猜你喜欢

转载自xls9577087.iteye.com/blog/2291488
EJB