无状态和有状态企业Java Bean

时间:2010-02-28 13:52:28

标签: java java-ee ejb ejb-3.1 stateful-session-bean

我正在阅读Java EE 6教程,我正在尝试理解无状态和有状态会话bean之间的区别。如果无状态会话bean在方法调用之间不保持状态,为什么我的程序按照它的方式运行?

package mybeans;

import javax.ejb.LocalBean;
import javax.ejb.Stateless;

@LocalBean
@Stateless
public class MyBean {

    private int number = 0;

    public int getNumber() {
        return number;
    }

    public void increment() {
        this.number++;
    }
}

客户

import java.io.IOException;
import javax.ejb.EJB;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;
import mybeans.MyBean;
import java.io.PrintWriter;

@WebServlet(name = "ServletClient", urlPatterns = { "/ServletClient" })
public class ServletClient extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @EJB
    MyBean mybean;

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        PrintWriter out = response.getWriter();
        mybean.increment();
        out.println(mybean.getNumber());
    }

}

我希望getNumber每次返回0,但它返回1并且在浏览器中重新加载servlet会增加更多。问题在于我对无状态会话bean的工作原理的理解,当然也不是库或应用服务器。有人可以给我一个简单的hello world类型的无状态会话bean示例,当你将它更改为有状态时,它的行为会有所不同吗?

7 个答案:

答案 0 :(得分:130)

无状态会话Bean(SLSB)没有绑定到一个客户端,并且无保证一个客户端与每个方法调用获取相同的实例(某些容器可能使用每个方法调用会话创建和销毁bean,这是一个特定于实现的决策,但实例通常是池化的 - 我没有提到集群环境。换句话说,尽管无状态bean可能具有实例变量,但这些字段并非特定于一个客户端,因此 远程呼叫之间不要依赖它们。

相比之下,有状态会话Bean(SFSB)终身专用到一个客户端,没有实例的交换或汇集(在钝化后可能会从内存中逐出,以节省资源但这是另一个故事)和维持会话状态。这意味着bean的实例变量可以在方法调用之间保持相对于客户端的数据。这使得相互依赖的方法调用成为可能(一种方法所做的更改会影响后续的方法调用)。多步骤流程(注册流程,购物车,预订流程......)是SFSB的典型用例。

还有一件事。如果您正在使用SFSB,那么必须避免将它们注入到多线程的类中,例如Servlet和JSF托管bean(您不希望它被所有客户端共享)。如果要在Web应用程序中使用SFSB,则需要执行JNDI查找并将返回的EJB实例存储在HttpSession对象中以供将来活动使用。这样的事情:

try {
    InitialContext ctx = new InitialContext();
    myStateful = (MyStateful)ctx.lookup("java:comp/env/MyStatefulBean");
    session.setAttribute("my_stateful", myStateful);
} catch (Exception e) {
    // exception handling
}

答案 1 :(得分:89)

重要的区别不是私有成员变量,而是将状态与特定用户相关联(想想“购物车”)。

有状态会话bean的状态就像servlet中的会话一样。即使没有Web客户端,有状态会话bean也允许您的应用仍然拥有该会话。当应用服务器从对象池中提取无状态会话bean时,它知道它可以用于满足任何请求,因为它不与特定用户相关联。

有状态会话bean必须首先发送给获得它的用户,因为他们的购物车信息应该只为他们所知。应用服务器确保这样做。想象一下,如果你可以开始购物,你的应用程序会有多受欢迎,然后当我出现时,应用服务器会向你发送有状态会话bean!

所以你的私人数据成员确实是“州”,但它不是“购物车”。尝试重做您的(非常好的)示例,使增量变量与特定用户相关联。增加它,创建一个新用户,看看他们是否仍然可以看到递增的值。如果操作正确,每个用户都应该只看到他们的计数器版本。

答案 2 :(得分:16)

在这种情况下,无国籍和有状态并不意味着你所期望的。

EJB的有状态是指我称之为会话状态。典型的例子是航班预订。如果它包含三个步骤:

  • 预备座位
  • 收取信用卡
  • 发行票据

想象一下,每个都是对会话bean的方法调用。有状态会话bean可以维护这种会话,以便记住调用之间发生的事情。

无状态会话bean没有会话状态的容量。

会话bean(无状态或有状态)中的全局变量完全是另一回事。有状态会话bean将创建一个bean池(因为bean一次只能在一个对话中使用),而无状态sesion bean通常只有一个实例,这将使全局变量起作用,但我不认为这必然得到保证。

答案 3 :(得分:4)

这件事发生是因为容器在池中只有一个bean实例,它被重用于所有调用。如果并行运行客户端,您将看到不同的结果,因为容器将在池中创建更多Bean实例。

答案 4 :(得分:3)

两种主要类型的会话bean之间的主要区别是:

无国籍豆

  1. 无状态会话Bean 是与调用其方法的客户端没有会话状态的那些。因此,他们可以创建一个对象池,可用于与多个客户端进行交互。
  2. 性能明智的无状态bean更好,因为它们没有每个客户端的状态。
  3. 他们可以并行处理来自多个客户端的多个请求。
  4. 有状态豆

    1. 有状态会话bean可以一次维护多个客户端的会话状态,并且不会在客户端之间共享任务。
    2. 会话结束后,不保留状态。
    3. 容器可以序列化并将状态存储为陈旧状态以供将来使用。这样做是为了节省应用程序服务器的资源并支持bean故障。

答案 5 :(得分:2)

它有很好的答案。我想补充一点小问题。无状态Bean不应该用于保存任何客户端数据。它应该用于模拟可以一次性完成的动作或过程"

答案 6 :(得分:2)

好问题

尝试以下代码(更改MyBean有状态/无状态):

import javax.ejb.LocalBean;
import javax.ejb.Stateful;
import javax.ejb.Stateless;

@LocalBean 
@Stateless 
public class MyBean {

    private int number = 0;

    public int getNumber() {
        return number;
    }

    public void increment() {
        this.number++;
    }
}

Servlet_1

 import java.io.IOException;
    import javax.ejb.EJB;
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.WebServlet;

    import java.io.PrintWriter;

    @WebServlet(name = "ServletClient", urlPatterns = { "/ServletClient" })
    public class ServletClient extends HttpServlet {

        private static final long serialVersionUID = 1L;

        @EJB
        MyBean mybean;

        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

            PrintWriter out = response.getWriter();
            mybean.increment();
            out.println(mybean.getNumber());
        }

    }

Servlet_2

import java.io.IOException;
import javax.ejb.EJB;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;

import java.io.PrintWriter;

@WebServlet(name = "NewServletClient", urlPatterns = { "/NewServletClient" })
public class NewServletClient extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @EJB
    MyBean mybean;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        PrintWriter out = response.getWriter();
        mybean.increment();
        out.println(mybean.getNumber());
    }

}

案例:MyBean-@ Stateless

http://localhost:8080/MYServletDemo/ ServletClient

1

http://localhost:8080/MYServletDemo/ ServletClient

2

http://localhost:8080/MYServletDemo_war_exploded/ newServletClient

3

http://localhost:8080/MYServletDemo/ ServletClient

4

案例:MyBean-@ Stateful

http://localhost:8080/MYServletDemo/ ServletClient

1

http://localhost:8080/MYServletDemo/ ServletClient

2

http://localhost:8080/MYServletDemo/ newServletClient

1

http://localhost:8080/MYServletDemo/ ServletClient

3