为什么无状态和有状态会话bean的行为不如预期

时间:2013-03-07 06:10:24

标签: ejb stateless stateful

public class Test1(){  
  public vod method1(){  
    try{  
        Hashtable<String, String> env = new Hashtable<String, String>();  
        env.put(Context.INITIAL_CONTEXT_FACTORY, EJB_JNDI_FACTORY);  
        env.put(Context.PROVIDER_URL, EJB_URL);  
        InitialContext ctx = new InitialContext(env);             
        LogSearchRemote logSearchRemote = (LogSearchRemote) ctx.lookup(LOG_SEARCH_EJB_BINDNAME);  
        System.out.println("logSearchRemote = " + logSearchRemote);  
        logSearchRemote.setTest(5);  
        System.out.println("logSearchRemote.getTest() = " + logSearchRemote.getTest());  
        System.out.println("logSearchRemote.getTestAgain() = " + logSearchRemote.getTestAgain());  
        LogSearchRemote logSearchRemote2 = (LogSearchRemote) ctx.lookup(LOG_SEARCH_EJB_BINDNAME);  
        System.out.println("logSearchRemote2 = " + logSearchRemote2);  
        System.out.println("logSearchRemote2.getTest() = " + logSearchRemote2.getTest());  
        System.out.println("logSearchRemote2.getTestAgain() = " + logSearchRemote2.getTestAgain());  
        this.session = session;  
        session.setAttribute("LogSearchEJB", logSearchRemote);  
        System.out.println("logSearchRemote = " + logSearchRemote);  
    }catch(Exception e){  
        e.printStackTrace();  
    }  
  // if @stateless, throw exception "$Proxy53 cannot be cast to hk.gov.ehr.service.tch.als.admin.logsearch.ejb.LogSearchRemote"  
  // if @stateful, no error!!  
LogSearchRemote logSearchRemote = (LogSearchRemote)session.getAttribute("LogSearchEJB");  
  //.....  
  }  
}   

1)对于上面的代码,如果LogSearchRemote实现bean是有状态的,那么

   LogSearchRemote logSearchRemote = (LogSearchRemote)session.getAttribute("LogSearchEJB");

没有错误,但如果LogSearchRemote实现bean是无状态,那么异常 “$ Proxy53不能转发给hk.gov.ehr.service.tch.als.admin.logsearch.ejb.LogSearchRemote” 扔了,为什么?

2)对于有状态会话bean ,我每次都找到

LogSearchRemote logSearchRemote = (LogSearchRemote) ctx.lookup(LOG_SEARCH_EJB_BINDNAME); 

返回不同的 logSearchRemote实现bean,
但是如果无状态会话bean,每次都是

LogSearchRemote logSearchRemote = (LogSearchRemote) ctx.lookup(LOG_SEARCH_EJB_BINDNAME);   

返回相同的 bean !!
为什么会这样呢? 我希望无状态会话bean不应该保持状态,每次查找应该返回一个不同的实现bean。

@Stateless(name = "AlsAdminLogSearch_1_0", mappedName = "ejb/AlsAdminLogSearch_1_0")  
public class LogSearchBean implements LogSearchRemote{  

    private int test;

    @Override  
    public void setTest(int value){  
        test = value;  
    }  

    @Override  
    public int getTest(){  
        return test;  
    }  

    @Override  
    public int getTestAgain(){  
        return test;  
    }  

//...methods  

}    

3)当我打电话时

logSearchRemote.setTest(5);  
System.out.println("logSearchRemote.getTest() = " + logSearchRemote.getTest());  
System.out.println("logSearchRemote.getTestAgain() = " 
logSearchRemote.getTestAgain());   

对于无状态会话bean,getTest()和getTestAgain()可以在之前的方法调用中记住实例变量“test”!!

为什么会记得?无状态会话bean不应该为每个方法调用调用不同的EJB实例吗?

1 个答案:

答案 0 :(得分:2)

无状态会话Bean应该“以无状态方式”使用,但事实上服务器会保留一组实例(取决于供应商策略)。因此,可能可能会在多次查找中收到完全相同的实例,但绝不保证。相反,不同的线程和客户端实例可以获得相同的无状态EJB实例,从而有效地共享此无状态EJB实例。这就是为什么无状态EJB不应该在内部持有任何“业务逻辑状态”,因为您不知道下一个客户端。虽然不推荐,但无状态EJB当然可以保留一些内部技术状态,这可以使其在实例重用时更快地访问外部“无状态”资源。 (请参阅Web和书籍中提供的EJB生命周期图。)

另一方面,有状态会话Bean应该“以有状态的方式”使用,即它们旨在在一段时间内保持状态并跨越多个客户端请求。因此,保证客户端在查找时接收“新实例”,并且由于容器无法确定在上一个使用周期结束后是否有任何实现“忘记”旧状态信息,因此容器在将客户端从客户端分离后会简单地销毁它并在查找时创建一个新实例。

问题在于有状态EJB 的客户端必须在整个计划使用周期中保持并保持其对EJB的引用,因为在丢失(last)引用后,Stateful EJB是在太空中丢失并被容器清理干净。另一方面,EJB ref不可序列化(据我所知),因此客户端需要在其EJB使用周期中序列化,以保持其EJB ref。< / p>

此外,我不知道任何与HttpSession属性查找相当的查找机制,以便在一段时间后查找任何现有的有状态EJB:有状态EJB实际上绑定到正在运行的客户端,或者它“不存在”。

因此,在使用有状态EJB来保存一些用户会话信息的项目中,我们决定使用HttpSession属性对象来保存有状态EJB引用。因此,应用程序可以在HttpSession中查找“EJB holder”对象,然后重用Stateful EJB。但是,如果Web容器决定序列化HttpSession属性对象(“EJB holder”),这显然会失败,因为EJB ref不能在序列化中存活。在HttpSession作用域中下一次查找“EJB holder”时,Web容器会对其进行反序列化,但Stateful EJB会被裁掉,不能再使用了。