java - 正确使用带有 Servlet 的有状态 Bean

标签 java servlets jakarta-ee ejb stateful

我们目前有一个注入(inject)到 Servlet 中的有状态 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 将检查它是否需要打开一个新的连接到 Report 中指定的数据库,之后根据参数构建的查询构建 HTML 格式的 Report指定。

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

最佳答案

有关 ConcurrentAccessException 的更多详细信息:根据 EJB 规范,对 SLSB 的访问由应用程序同步。服务器。但是,SFSB 并非如此。确保 SFSB 不会被同时访问的负担落在了应用程序开发人员的肩上。

为什么?好吧,SLSB 的同步只在实例级是必要的。也就是说,SLSB 的每个特定实例都是同步的,但您可能在一个池中或集群中的不同节点上有多个实例,并且不同实例上的并发请求不是问题。不幸的是,由于实例的钝化/激活和跨集群的复制,使用 SFSB 并不容易。这就是规范不强制执行此操作的原因。看看this dicussion如果您对该主题感兴趣。

这意味着从 servlet 使用 SFSB 很复杂。具有来自同一 session 的多个窗口的用户,或在呈现完成之前重新加载页面可能会导致并发访问。在 servlet 中完成的对 EJB 的每次访问理论上都需要在 bean 本身上同步。我所做的是创建一个 InvocationHandler同步特定 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。

编辑:

此答案反射(reflect)了 EJB 3.0 规范(第 4.3.13 节):

Clients are not allowed to make concurrent calls to a stateful session object. If a client-invoked business method is in progress on an instance when another client-invoked call, from the same or different client, arrives at the same instance of a stateful session bean class, if the second client is a client of the bean’s business interface, the concurrent invocation may result in the second client receiving the javax.ejb.ConcurrentAccessException

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

By default, clients are allowed to make concurrent calls to a stateful session object and the container is required to serialize such concurrent requests.

[...]

The Bean Developer may optionally specify that concurrent client requests to a stateful session bean are prohibited. This is done using the @AccessTimeout annotation or access-timeout deployment descriptor element with a value of 0. In this case, if a client-invoked business method is in progress on an instance when another client-invoked call, from the same or different client, arrives at the same instance of a stateful session bean, if the second client is a client of the bean’s business interface or no-interface view, the concurrent invocation must result in the second client receiving a javax.ejb.ConcurrentAccessException

关于java - 正确使用带有 Servlet 的有状态 Bean,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1935178/

相关文章:

java - 在servlet中读取和写入文本文件

java - 类似于 Spring Data JPA 或 Hades for CDI 或 EJB 的框架?

java - 为什么找不到 getservletcontext?

jakarta-ee - Java EE应用程序中不同版本的lib

jakarta-ee - 多个 WAR 试图加载彼此的类

java - GWT 中的异步调用是否会由于另一个需要很长时间才能完成的异步调用而被阻止

java - 定制 Java 包 JAXB wsimport

java - 在java中,从另一个数字字符串中减去1个数字字符串的最佳方法是什么

java - 递归迷宫求解器方法问题

java - 数据未插入 MySQL 数据库