有状态、无状态和单例 EJB bean 的结果相同

Same result from stateful, stateless and singleton EJB beans

我正在尝试制作一个@Stateful、@Stateless 和@Singleton EJB bean 的简单示例,以帮助我更好地理解它们之间的差异。问题是,当我使用任何@Stateful、@Stateless 或@Singleton 注释对 bean 进行注释时,根本没有区别。

这是豆子:

import javax.ejb.Singleton;
import javax.ejb.Stateful;
import javax.ejb.Stateless;


    @Stateful
    public class Bean {
        private int counter = 0;

        public int getCounter(){
            return counter++;
        }  
    }

这是 Servlet 客户端:

import java.io.IOException;
import java.io.PrintWriter;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import javaeetutorial.converter.ejb.Bean;

@WebServlet(urlPatterns="/")
public class Client extends HttpServlet{

    @EJB
    Bean bean;

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.println("<html lang=\"en\">");
        out.println("<head>");
        out.println("<title>test</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Servlet ConverterServlet at " +
                request.getContextPath() + "</h1>");
        try {
            out.println("<form method=\"get\">");
            out.println("<input type=\"submit\" value=\"Submit\">");
            out.println("</form>");
            out.println("<p>" + bean.getCounter() + "</p>");    
            out.println("<p>" + bean + "</p>"); 
        } finally {
            out.println("</body>");
            out.println("</html>");
            out.close();
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

}

我的预期结果是:

@Statefull - 当每个不同的客户端按下按钮以查看从 0 开始计数时

@Singleton - 当任何客户端按下按钮时只看到一个计数

@Stateless - 我不知道会发生什么

预期结果是:

  • @Stateful - 有状态会话 bean 有超时,所以你应该永远不要 将有状态会话 bean 注入到一个长期存在的对象中,例如 servlet。相反,您应该只查找并使用它们(可能通过将引用插入到 HttpSession 中)。预期的结果是 bean 实例及其计数器将被所有请求共享,如果您发出并发请求,每个请求都会阻塞等待访问有状态会话 bean(尝试在 getCounter 方法中添加一个睡眠,然后发出请求来自多个浏览器 tabs/windows)。如果你在一段时间内没有发出请求,bean 将被移除,这将导致所有后续请求失败;您可以使用 @StatefulTimeout(value=1, unit=TimeUnit.SECONDS) 轻松观察这一点。

  • @Singleton - bean 实例及其计数器将由所有请求共享。默认情况下,单例使用容器管理的并发,因此在一个方法中一次只允许一个请求。同样,您可以在 getCounter 方法中添加一个睡眠来观察这一点。

  • @Stateless - 根据您的应用程序服务器的池配置,可能会根据并发请求的需要创建多个 bean 实例,并且 bean 实例可能会跨请求重用。同样,您可以向 getCounter 方法添加睡眠,并从多个浏览器发出请求 tabs/windows 以观察这一点。无状态会话 bean 旨在用于封装其他 EJB 服务(例如,事务、安全、调度等),因此成员变量应该只用于缓存可以使用的状态,而不管客户端如何(例如,缓存对DataSource, UserTransaction等), 不存储状态。

你遇到的问题是 webservlet 被实例化一次(或者至少不是每个请求实例化一次,这是你需要清楚地看到区别的地方),所以它是 bean 实例。因此,您使用的是哪种类型的 bean(有状态、无状态、单例)没有区别,因为 Bean 实例始终依赖于同一个 servlet 实例。

如果您使用 requestScoped 资源,通过 singleton 注释,您将计算计数器被请求的次数(全部请求共享同一个实例),使用 stateful 你会看到客户端请求计数器的次数,使用 stateless 则视情况而定。但是,因为计数器是一个状态,所以使用无状态 bean 没有意义(看名称)。您可以将无状态理解为 pooled ejb,因此每次需要该 bean 时,服务器都会获取无状态池的一个实例。一个优点是 bean 的性能,因为它们已经创建到池中(检查无状态池配置)它们使用起来非常快。

您可以找到更多信息here