0
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 implementation bean 是stateless,则抛出异常“$Proxy53 cannot be cast to 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 实例吗?

4

1 回答 1

1

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

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

这样做的问题是,有状态 EJB 的客户端必须在整个计划的使用周期内持有并保持其对 EJB 的引用,因为在丢失(最后一个)引用之后,有状态 EJB 会丢失在空间中并被容器清理。另一方面,EJB ref 是不可序列化的(据我所知),因此要求客户端在其 EJB 使用周期期间序列化,以保持其 EJB ref 处于活动状态。

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

因此,在一个使用有状态 EJB 来保存一些用户会话信息的项目中,我们决定使用 HttpSession 属性对象来保存有状态 EJB 引用。因此,应用程序可以在 HttpSession 中查找“EJB 持有者”对象,然后重用 Stateful EJB。但是,如果 Web 容器决定序列化 HttpSession 属性对象(“EJB 持有者”),这显然会失败,因为 EJB ref 无法在序列化后继续存在。下次在 HttpSession 范围内查找“EJB 持有者”时,它会被 Web 容器反序列化,但有状态 EJB 被切断其 ref 并且不能再使用。

于 2013-05-16T16:35:32.360 回答