Bur*_*dar 8 java servlets ejb stateful java-ee
我们目前有一个注入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,使用每个注入的bean实例重复打开和关闭数据库连接似乎非常低效.
ewe*_*nli 14
有关ConcurrentAccessException的更多详细信息:根据EJB规范,应用程序会同步对SLSB的访问.服务器.但是,SFSB并非如此.确保不同时访问SFSB的负担在应用程序开发人员的肩上.
为什么?好吧,只有在实例级别才需要同步SLSB.也就是说,SLSB的每个特定实例都是同步的,但是您可能在池中或群集中的不同节点上有多个实例,并且不同实例上的并发请求不是问题.遗憾的是,由于实例的钝化/激活以及跨群集的复制,这对于SFSB来说并不那么容易.这就是规范没有强制执行此操作的原因.如果您对该主题感兴趣,请查看此讨论.
这意味着从servlet使用SFSB很复杂.具有来自同一会话的多个窗口的用户,或者在渲染完成之前重新加载页面可以导致并发访问.理论上,对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.
编辑:
这个答案反映了EJB 3.0规范(第4.3.13节):
不允许客户端对有状态会话对象进行并发调用.如果客户端调用的业务方法正在实例上,当来自相同或不同客户端的另一个客户端调用的调用到达有状态会话bean类的同一实例时,如果第二个客户端是bean的业务的客户端接口,并发调用可能导致第二个客户端收到javax.ejb.ConcurrentAccessException
EJB 3.1(第4.3.13节)中已删除此类限制:
默认情况下,允许客户端对有状态会话对象进行并发调用,并且需要容器序列化此类并发请求.
[...]
Bean Developer可以选择指定禁止对有状态会话bean的并发客户端请求.这是使用值为0的@AccessTimeout批注或访问超时部署描述符元素完成的.在这种情况下,如果客户端调用的业务方法正在实例上进行另一个客户端调用的调用,来自相同或不同的客户端,到达有状态会话bean的同一个实例,如果第二个客户端是bean的业务接口或无接口视图的客户端,并发调用必须导致第二个客户端接收到javax.ejb.ConcurrentAccessException
这不是有意使用的有状态会话bean(SFSB).它们被设计为保持对话状态,并且被绑定到用户的http会话以保持该状态,就像直接在会话中存储状态的重量级替代.
如果你想保存数据库连接之类的东西,那么有更好的方法可以解决它.
最好的选择是使用连接池.您应该始终使用连接池,如果您在应用程序服务器内运行(如果您使用的是EJB,那么您可以使用它),那么您可以轻松地使用appserver的数据源配置来创建连接池,并使用在无状态会话bean(SLSB)中.