无国籍和有国籍的 EJB

我正在学习 JavaEE6教程,并试图理解无状态会话 bean 和有状态会话 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 更改为有状态时,它的行为会有所不同?

99897 次浏览

The important difference is not private member variables, but associating state with a particular user (think "shopping cart").

The stateful piece of stateful session bean is like the session in servlets. Stateful session beans allow your app to still have that session even if there isn't a web client. When the app server fetches a stateless session bean out of the object pool, it knows that it can be used to satisfy ANY request, because it's not associated with a particular user.

A stateful session bean has to be doled out to the user that got it in the first place, because their shopping cart info should be known only to them. The app server ensures that this is so. Imagine how popular your app would be if you could start shopping and then the app server gave your stateful session bean to me when I came along!

So your private data member is indeed "state", but it's not "shopping cart". Try to redo your (very good) example to make it so the incremented variable is associated with a particular user. Increment it, create a new user, and see if they can still see the incremented value. If done correctly, every user should see just their version of the counter.

Stateless and stateful in this context don't mean quite what you might expect.

Statefulness with EJBs refers to what I call conversational state. The classic example is a flight booking. If it consists of three steps:

  • Reserve seat
  • Charge credit card
  • Issue Ticket

Imagine each of those is a method call to a session bean. A stateful session bean can maintain this kind of conversation so it remembers what happens between calls.

Stateless session beans don't have such capacity for conversational state.

Global variables inside a session bean (stateless or stateful) are something else entirely. Stateful session beans will have a pool of beans created (since a bean can only be used in one conversation at a time) whereas stateless sesion beans will often only have one instance, which will make the global variable works, but I don't think this is necessarily guaranteed.

Stateless Session Beans (SLSB) are not tied to one client and there is no guarantee for one client to get the same instance with each method invocation (some containers may create and destroy beans with each method invocation session, this is an implementation-specific decision, but instances are typically pooled - and I don't mention clustered environments). In other words, although stateless beans may have instance variables, these fields are not specific to one client, so don't rely on them between remote calls.

In contrast, Stateful Session Beans (SFSB) are dedicated to one client for their entire life, there is no swapping or pooling of instances (it may be evicted from memory after passivation to save resources but that's another story) and maintain conversational state. This means that the instance variables of the bean can keep data relative to the client between method invocations. And this makes possible to have interdependent method calls (changes made by one method affect subsequent method calls). Multi-step processes (a registration process, a shopping cart, a booking process...) are typical use cases for SFSB.

One more thing. If you are using SFSB, then you must avoid injecting them into classes that are multithreaded in nature, such as Servlets and JSF managed beans (you don't want it to be shared by all clients). If you want to use SFSB in your web application, then you need to perform a JNDI lookup and store the returned EJB instance in the HttpSession object for future activity. Something like that:

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

This thing happen because the container only has one bean instance in the pool that is being reused for all calls. If you run the clients in parallel you will see a different result because the container will create more bean instances in the pool.

It has good answers. I would like to add small answer. Stateless Bean should not used to hold any client data. It should be used to "to model actions or processes that can be done in one shot".

The major differences between the two major types of session beans are:

Stateless Beans

  1. Stateless Session Beans are the ones which have no conversational state with the client which has called its methods. For this reason they can create a pool of objects which can be used to interact with multiple clients.
  2. Performance wise stateless beans are better since they don't have states per client.
  3. They can handle multiple requests from multiple clients in parallel.

Stateful Beans

  1. Stateful session beans can maintain the conversational state with multiple clients at a time and the task is not shared between the clients.
  2. After the session is completed the state is not retained.
  3. The container can serialize and store the state as a stale state for future use. This is done to save resources of the application server and to support bean failures.

Good Question,

try this code (change MyBean Stateful/Stateless.):

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());
}


}

case : 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

case : 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