正确使用 Servlet 中的 Stateful Bean

2024-04-20

目前,我们有一个注入到 Servlet 中的 Stateful bean。问题是有时我们会得到一个Caused by: javax.ejb.ConcurrentAccessException: SessionBean is executing another request. [session-key: 7d90c02200a81f-752fe1cd-1]在有状态 bean 上执行方法时。

public class NewServlet extends HttpServlet {  
    @EJB  
    private ReportLocal reportBean;

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
           String[] parameters  = fetchParameters(request);
           out.write(reportBean.constructReport(parameters));
        } finally { 
            out.close();
        }
    } 
}

在上面的代码中,constructReport将检查是否需要打开与报告中指定的数据库的新连接,然后根据指定参数构建的查询构建 HTML 格式的报告。

我们选择使用有状态 Bean 而不是无状态 Bean 的原因是因为我们需要打开到未知数据库的数据库连接并对其执行查询。对于无状态 bean,重复打开和关闭与每个注入的 bean 实例的数据库连接似乎效率极低。


有关的更多详细信息并发访问异常 http://docs.oracle.com/javaee/6/api/javax/ejb/ConcurrentAccessException.html:根据 EJB 规范,对 SLSB 的访问由应用程序同步。服务器。然而,SFS​​B 的情况并非如此。确保 SFSB 不会被同时访问的重担落在了应用程序开发人员的肩上。

为什么?那么,SLSB 的同步仅在实例级别是必要的。也就是说,SLSB 的每个特定实例都是同步的,但您可能在池中或集群中的不同节点上有多个实例,并且不同实例上的并发请求不是问题。遗憾的是,由于实例的钝化/激活以及跨集群的复制,这对于 SFSB 来说并不那么容易。这就是为什么规范不强制执行这一点。看一下这次讨论 https://jira.jboss.org/jira/browse/JBAS-1443如果您对该主题感兴趣。

这意味着从 servlet 使用 SFSB 很复杂。用户在同一会话中拥有多个窗口,或者在渲染完成之前重新加载页面可能会导致并发访问。理论上,在 servlet 中完成的对 EJB 的每次访问都需要在 bean 本身上进行同步。我所做的是创建一个调用处理程序 http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/InvocationHandler.html同步特定 EJB 实例上的所有调用:

public class SynchronizationHandler implements InvocationHandler {

 private Object target;  // the EJB

 public SynchronizationHandler( Object bean )
 {
        target = bean;
 }

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
  {
    synchronized( target )
    {
       // invoke method to the target EJB
    }
  }

}

然后,在获得对 EJB 的远程引用后,立即用SynchronizationHandler。这样,您就可以确保您的应用程序不会同时访问该特定实例(只要它仅在一个 JVM 中运行)。您还可以编写一个常规包装类来同步 bean 的所有方法。

尽管如此,我的结论是:尽可能使用 SLSB。

EDIT:

这个答案反映了 EJB 3.0 规范(第 4.3.13 节):

不允许客户端对有状态会话进行并发调用 目的。如果客户端调用的业务方法正在进行 例如,当另一个客户端调用来自相同或不同的调用时 客户端,到达有状态会话 Bean 类的同一个实例, 如果第二个客户端是 bean 业务接口的客户端,则 并发调用可能会导致第二个客户端接收到 javax.ejb.ConcurrentAccessException

EJB 3.1(第 4.3.13 节)中删除了此类限制:

默认情况下,允许客户端并发调用有状态的 会话对象和容器需要序列化这样的 并发请求。

[...]

Bean 开发人员可以选择指定并发客户端 禁止向有状态会话 bean 发出请求。这是使用完成的 @AccessTimeout 注释或访问超时部署描述符 值为0的元素。此时,如果客户端调用的业务 当另一个客户端调用时,方法正在实例上进行, 来自相同或不同的客户端,到达同一实例 有状态会话 Bean,如果第二个客户端是该 Bean 的客户端 业务接口或无接口视图、并发调用 必须导致第二个客户端收到 javax.ejb.ConcurrentAccessException

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

正确使用 Servlet 中的 Stateful Bean 的相关文章

随机推荐