有状态无状态是和用户结合在一起理解和使用的,也就是是否有状态是相对于用户来讲的,比如下面代码:
package cn.edu.ustc.ejbs.impl;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import cn.edu.ustc.ejbs.StatelessEJB;
@Stateless(mappedName = "StatelessEJBImpl")
@Remote
public class StatelessEJBImpl implements StatelessEJB{//定义一个无状态的会话bean
private int value=0;
public void sayHello(int value) {
System.out.println("Hello,your value is:" + value);
System.out.println("your last value is:" + this.value);
this.value = value;
}
}
package cn.edu.ustc.ejbs.impl;
import javax.ejb.Remote;
import javax.ejb.Stateful;
import cn.edu.ustc.ejbs.StatefulEJB;
@Stateful(mappedName = "StatefulEJBImpl")
@Remote
public class StatefulEJBImpl implements StatefulEJB{//定义一个有状态的会话bean
private int value=0;
public void sayHello(int value) {
System.out.println("Hello,your value is:" + value);
System.out.println("your last value is:" + this.value);
this.value = value;
}
}
客户端(用户)代码:
InitialContext ctx=new InitialContext();
StatelessEJB hello0 = (StatelessEJB) ctx.lookup("StatelessEJBImpl#cn.edu.ustc.ejbs.StatelessEJB");
hello0.sayHello(1);
hello0.sayHello(2);
StatelessEJB hello1 = (StatelessEJB) ctx.lookup("StatelessEJBImpl#cn.edu.ustc.ejbs.StatelessEJB");
hello1.sayHello(1);
hello1.sayHello(2);
StatefulEJB hello2 = (StatefulEJB) ctx.lookup("StatefulEJBImpl#cn.edu.ustc.ejbs.StatefulEJB");
hello2.sayHello(1);
hello2.sayHello(2);
StatefulEJB hello3 = (StatefulEJB) ctx.lookup("StatefulEJBImpl#cn.edu.ustc.ejbs.StatefulEJB");
hello3.sayHello(1);
hello3.sayHello(2);
输出结果如下:
Hello,your value is:1
your last value is:2
Hello,your value is:2
your last value is:1
--
Hello,your value is:1
your last value is:2
Hello,your value is:2
your last value is:1
--------
Hello,your value is:1
your last value is:0
Hello,your value is:2
your last value is:1
--
Hello,your value is:1
your last value is:0
Hello,your value is:2
your last value is:1
可以看出有状态的会话bean在同一个类的实例访问的时候会保存上一次访问的状态,hello2和hello3是不同的两个实例,而容器会为不同的实例生成一个该实例唯一使用的会话bean,所以调用sayHello的结果是一样的。而无状态会话bean的状态(i)的值是无意义的,不管哪个实例访问都会改变i的值。
再来看看他们的定义:
有状态会话bean :每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。
无状态会话bean :bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean 并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。
即不能以bean中有无属性等来区分是否有状态的,有状态无状态并非针对session bean中的状态(成员变量or属性)。
对于有状态会话Bean来说,只要有客户端发送对有状态会话Bean的访问,服务器都会创建一个会话Bean实例与该客户端对应,这样这个实例与这个客户端就是一一对应的。如果客户端在Bean实例中保存了信息,之后还可以使用。
而对于无状态会话Bean来说,服务器端会维持一个实例池,创建好若干个实例对象供客户端调用。当从客户端发送创建会话Bean的请求时,并不一定会真的创建 EJB,多数情况下是从实例池中得到一个实例,用完之后重新放回实例池。如果下次再访问,再从实例池中取出一个实例使用,并不一定是上次的实例。即使两次访问使用的是同一个实例,在两次访问之间也有可能有其他的客户端访问了该实例。所以,并不能保证在多次访问之间的信息会被保存。因此无状态会话Bean不会专门保存也没必要客户端的信息。是否有必要建立一个实例池?spring的singleton bean也能很好的满足不同用户的每次请求。以目前的知识了解个人暂且认为在session bean方法里面没有需要同步的地方时,没必要维持一个实例池。刚才了解到EJB3.1确实增加了singleton session bean。而在我的测试例子中,Stateless bean好像每次都是同一个实例,这样才会导致后面几次访问都能得到上一次访问中的值,跟singleton一样。
至于有状态和无状态的性能,因为有状态会话Bean需要保存特定客户端的信息,一个客户端对应一个实例,既是在当时客户端有连接没有访问的情况下,也要为这个客户端保留这个实例。这样随着客户端数量的增加,服务器端需要创建的实例的数量也在增加,增加到一定程度对服务器的性能就会有一定的影响。为了不对服务器的性能产生影响,通常服务器会进行一些优化。当客户端的数量超过某个值之后,就不创建新的实例。虽然不创建新的实例,还是需要对用户响应,这时候就采用共享实例的方式。会查看哪个 实例虽然处于连接状态,但是没有访问,然后把这个实例的状态保存起来,使用这个实例为新的请求服务,对于原来的客户端来说,称为挂起。如果原来的客户端又发送请求了,会重新查找一个空闲的实例并且把已经保存好的状态恢复回来,这个过程称为激活。所以在有状态会话Bean的访问过程,经常会发生查找实例,激活挂起等操作,所以效率比较低。而发送对无状态会话Bean的请求的时候,可以随便取一个空闲的实例为客户端服务,所以效率比较高。
有状态会话Bean的好处是,可以保存客户端的状态,所以客户端在后续访问的时候就可以少传递一些参数。而无状态会话Bean需要传递方法执行过程中需要的所有参数。
咱们自己用的时候可以根据上面分析的有状态会话Bean和无状态会话Bean的优缺点来决定。如果要频繁的访问,并且多次访问之间会共享一些信息,这时候应该使用有状态会话Bean。对于不经常使用的功能,可以使用无状态会话Bean。而无状态会话Bean的使用要比有状态会话Bean的使用多。