Beans Java empresariales sin estado y con estado

93

Estoy revisando el tutorial de Java EE 6 y estoy tratando de entender la diferencia entre beans de sesión sin estado y con estado. Si los beans de sesión sin estado no retienen su estado entre llamadas a métodos, ¿por qué mi programa está actuando como lo hace?

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++;
    }
}

El cliente

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

}

Esperaba que getNumber devolviera 0 cada vez, pero devuelve 1 y las recargas del servlet en mi navegador lo aumentan más. El problema es mi comprensión de cómo funcionan los beans de sesión sin estado y no con las bibliotecas o el servidor de aplicaciones, por supuesto. ¿Alguien puede darme un ejemplo simple tipo hola mundo de un bean de sesión sin estado que se comporta de manera diferente cuando lo cambia a estado?

Stanley Kelly
fuente
6
Relacionado: stackoverflow.com/questions/8887140/… Esta respuesta es quizás más sencilla de entender. Tenga en cuenta que los servlets son básicamente de ámbito de aplicación (solo hay 1 instancia de servlet en toda la aplicación que se comparte / reutiliza en todas las solicitudes / sesiones HTTP.
BalusC
hola, primero incrementa y luego obtiene el valor ... así que no puede esperar un valor de 0.
rzur2004
Solo quiero agradecerle por preguntar esto, aborda mi problema en este momento. No podría haber preguntado eso mejor
kholofelo Maloma

Respuestas:

93

La diferencia importante no son las variables de miembros privados, sino la asociación del estado con un usuario en particular (piense en "carrito de compras").

La pieza con estado del bean de sesión con estado es como la sesión en los servlets. Los beans de sesión con estado permiten que su aplicación aún tenga esa sesión incluso si no hay un cliente web. Cuando el servidor de aplicaciones obtiene un bean de sesión sin estado del grupo de objetos, sabe que se puede usar para satisfacer CUALQUIER solicitud, porque no está asociado con un usuario en particular.

Un bean de sesión con estado debe distribuirse al usuario que lo obtuvo en primer lugar, porque la información de su carrito de compras debe ser conocida solo por ellos. El servidor de la aplicación se asegura de que sea así. ¡Imagínese lo popular que sería su aplicación si pudiera comenzar a comprar y luego el servidor de la aplicación me diera su bean de sesión con estado cuando llegué!

Entonces, su miembro de datos privados es de hecho "estado", pero no es "carrito de compras". Intente rehacer su (muy bueno) ejemplo para que la variable incrementada esté asociada con un usuario en particular. Increméntelo, cree un nuevo usuario y vea si todavía pueden ver el valor incrementado. Si se hace correctamente, cada usuario debería ver solo su versión del contador.

duffymo
fuente
¿Puede proporcionar en un comentario una respuesta explícita? ¿Por qué siempre el bean sin estado en este ejemplo mantiene el valor y lo aumenta cada vez? ¿Porque solo hay un usuario?
arjacsoh
2
El contador aumentará independientemente del número de usuarios. Entonces, si el usuario1 entra e incrementa el contador a 1 y simultáneamente el usuario2 entra y lo incrementa, el valor será 2. En realidad debería mostrar que el usuario1 tiene 1 y el usuario2 tiene 1 (si eso es lo que pretende hacer. ejemplo como arriba).
Krishna
137

Los beans de sesión sin estado (SLSB) no están vinculados a un cliente y no hay garantía de que un cliente obtenga la misma instancia con cada invocación de método (algunos contenedores pueden crear y destruir beans con cada sesión de invocación de método, esta es una decisión específica de implementación , pero las instancias generalmente se agrupan, y no menciono los entornos agrupados). En otras palabras, aunque los beans sin estado pueden tener variables de instancia, estos campos no son específicos de un cliente, así que no confíe en ellos entre llamadas remotas.

Por el contrario, los Stateful Session Beans (SFSB) están dedicados a un cliente durante toda su vida, no hay intercambio ni agrupación de instancias (puede ser desalojado de la memoria después de la pasivación para ahorrar recursos, pero esa es otra historia) y mantener el estado de conversación . Esto significa que las variables de instancia del bean pueden mantener datos relativos al cliente entre invocaciones de métodos. Y esto hace posible tener llamadas de método interdependientes (los cambios realizados por un método afectan las llamadas de método posteriores). Los procesos de varios pasos (un proceso de registro, un carrito de compras, un proceso de reserva ...) son casos de uso típicos para SFSB.

Una cosa más. Si está utilizando SFSB, debe evitar inyectarlos en clases de naturaleza multiproceso, como Servlets y beans administrados por JSF (no desea que todos los clientes lo compartan). Si desea utilizar SFSB en su aplicación web, debe realizar una búsqueda JNDI y almacenar la instancia EJB devuelta en el HttpSessionobjeto para la actividad futura. Algo como eso:

try {
    InitialContext ctx = new InitialContext();
    myStateful = (MyStateful)ctx.lookup("java:comp/env/MyStatefulBean");
    session.setAttribute("my_stateful", myStateful);
} catch (Exception e) {
    // exception handling
}
Pascal Thivent
fuente
Gracias por la limpieza. Cuando utilizo un programa de línea de comandos independiente para el cliente, es obvio ver la diferencia.
Stanley Kelly
gracias por tus comentarios, son más esclarecedores. Primero, dé la definición abstracta, luego especifique algunos casos de uso para cada situación y luego señale algunas trampas. Genial +1
arthur
¿La parte para evitar inyectar también sale para EJB 3.1?
jacktrades
7
@Pascal si "Stateful Session Beans (SFSB) están dedicados a un cliente durante toda su vida", es decir, esta capacidad está integrada en SFSB, entonces ¿por qué es necesario almacenarlos en el objeto HttpSession?
user1169587
2
¿Por qué necesitamos mantener el bean con estado en sesión si ya está 'sesionado'? De esta manera podemos hacer que todos los objetos estén sesionados. Explicar por favor
Georgy Gobozov
18

Sin estado y sin estado en este contexto no significa exactamente lo que podría esperar.

Statefulness con EJB se refiere a lo que yo llamo estado conversacional . El ejemplo clásico es la reserva de un vuelo. Si consta de tres pasos:

  • Reserva de asiento
  • Cargar tarjeta de crédito
  • Boleto de emisión

Imagine que cada uno de ellos es una llamada a un método a un bean de sesión. Un bean de sesión con estado puede mantener este tipo de conversación para recordar lo que sucede entre llamadas.

Los beans de sesión sin estado no tienen tal capacidad para el estado conversacional.

Las variables globales dentro de un bean de sesión (sin estado o con estado) son algo completamente diferente. Los beans de sesión con estado tendrán un grupo de beans creado (ya que un bean solo se puede usar en una conversación a la vez) mientras que los beans de sesión sin estado a menudo solo tendrán una instancia, lo que hará que la variable global funcione, pero no creo esto está necesariamente garantizado.

cletus
fuente
5

Las principales diferencias entre los dos tipos principales de beans de sesión son:

Frijoles sin estado

  1. Los beans de sesión sin estado son los que no tienen ningún estado de conversación con el cliente que ha llamado a sus métodos. Por esta razón, pueden crear un grupo de objetos que se pueden utilizar para interactuar con varios clientes.
  2. Los beans sin estado en cuanto al rendimiento son mejores ya que no tienen estados por cliente.
  3. Pueden manejar múltiples solicitudes de múltiples clientes en paralelo.

Frijoles con estado

  1. Los beans de sesión con estado pueden mantener el estado conversacional con varios clientes a la vez y la tarea no se comparte entre los clientes.
  2. Una vez completada la sesión, el estado no se conserva.
  3. El contenedor puede serializar y almacenar el estado como un estado obsoleto para uso futuro. Esto se hace para ahorrar recursos del servidor de aplicaciones y para soportar fallos de bean.
Pritam Banerjee
fuente
4

Esto sucede porque el contenedor solo tiene una instancia de bean en el grupo que se reutiliza para todas las llamadas. Si ejecuta los clientes en paralelo, verá un resultado diferente porque el contenedor creará más instancias de bean en el grupo.

Neyma
fuente
4

Tiene buenas respuestas. Me gustaría agregar una pequeña respuesta. Stateless Bean no debe utilizarse para almacenar datos de clientes. Debe utilizarse para "modelar acciones o procesos que se pueden realizar de una vez".

Malatesh
fuente
4

Buena pregunta,

pruebe este código (cambie 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());
    }

}

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

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

ZURA Tikaradze
fuente
1
¡Sí, eso es todo y funciona! Explicación muy fácil, gracias!
Nesquik27