Java Beans sem estado e com estado

93

Estou seguindo o tutorial Java EE 6 e estou tentando entender a diferença entre beans de sessão sem estado e com estado. Se os beans de sessão sem estado não retêm seu estado entre as chamadas de método, por que meu programa está agindo da maneira que está?

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

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

}

Eu esperava que getNumber retornasse 0 todas as vezes, mas está retornando 1 e as recargas do servlet em meu navegador o aumentam ainda mais. O problema está no meu entendimento de como funcionam os beans de sessão sem estado e não com as bibliotecas ou o servidor de aplicativos, é claro. Alguém pode me dar um exemplo simples do tipo hello world de um bean de sessão sem estado que se comporta de maneira diferente quando você o altera para com estado?

Stanley Kelly
fonte
6
Relacionado: stackoverflow.com/questions/8887140/… Esta resposta talvez seja mais simples de entender. Observe que os servlets têm basicamente o escopo do aplicativo (há apenas 1 instância de servlet em todo o aplicativo que é compartilhada / reutilizada em todas as solicitações / sessões HTTP.
BalusC
oi, Você primeiro incrementa e depois obtém o valor .... então você não pode esperar um valor de 0.
rzur2004
Só quero agradecer por perguntar isso, pois trata do meu problema no momento. Eu não poderia ter perguntado melhor
kholofelo Maloma

Respostas:

93

A diferença importante não são as variáveis ​​de membro privadas, mas a associação de estado a um usuário específico (pense em "carrinho de compras").

A parte com estado do bean de sessão com estado é como a sessão em servlets. Os beans de sessão com estado permitem que seu aplicativo ainda tenha aquela sessão, mesmo se não houver um cliente web. Quando o servidor de aplicativos busca um bean de sessão stateless fora do pool de objetos, ele sabe que pode ser usado para satisfazer QUALQUER solicitação, porque não está associado a um usuário específico.

Um bean de sessão stateful deve ser distribuído ao usuário que o obteve em primeiro lugar, porque as informações do carrinho de compras devem ser conhecidas apenas por ele. O servidor de aplicativos garante isso. Imagine o quão popular seu aplicativo seria se você pudesse começar a comprar e o servidor de aplicativos me desse seu bean de sessão com estado quando eu aparecesse!

Portanto, seu membro de dados privados é de fato "estado", mas não é "carrinho de compras". Tente refazer seu (muito bom) exemplo para torná-lo de forma que a variável incrementada seja associada a um usuário específico. Incremente-o, crie um novo usuário e veja se ele ainda pode ver o valor incrementado. Se feito corretamente, cada usuário deve ver apenas sua versão do contador.

duffymo
fonte
Você pode fornecer em um comentário uma resposta explícita? Por que sempre o bean sem estado neste exemplo mantém o valor e o aumenta a cada vez? Porque existe apenas um usuário?
arjacsoh
2
O contador aumentará independentemente do número de usuários. Portanto, se o usuário1 entrar e incrementar o contador para 1 e simultaneamente o usuário2 entrar e incrementá-lo, o valor será 2. Na verdade, deve mostrar que o usuário1 tem 1 e o usuário2 tem 1 (se é isso que você pretende fazer. Carrinho de compras exemplo como acima).
Krishna de
137

Beans de sessão sem estado (SLSB) não estão vinculados a um cliente e não há garantia de que um cliente obtenha a mesma instância com cada chamada de método (alguns contêineres podem criar e destruir beans com cada sessão de chamada de método, esta é uma decisão específica da implementação , mas as instâncias são normalmente agrupadas - e não menciono os ambientes em cluster). Em outras palavras, embora os beans sem estado possam ter variáveis ​​de instância, esses campos não são específicos para um cliente, portanto, não conte com eles entre chamadas remotas.

Em contraste, Stateful Session Beans (SFSB) são dedicados a um cliente por toda a sua vida, não há troca ou agrupamento de instâncias (pode ser removido da memória após a passivação para economizar recursos, mas isso é outra história) e manter o estado de conversação . Isso significa que as variáveis ​​de instância do bean podem manter os dados relativos ao cliente entre as chamadas de método. E isso torna possível ter chamadas de método interdependentes (as alterações feitas por um método afetam chamadas de método subsequentes). Processos de várias etapas (um processo de registro, um carrinho de compras, um processo de reserva ...) são casos de uso típicos para SFSB.

Mais uma coisa. Se estiver usando SFSB, você deve evitar injetá-los em classes que são multithreaded por natureza, como Servlets e beans gerenciados JSF (você não quer que eles sejam compartilhados por todos os clientes). Se quiser usar o SFSB em seu aplicativo da web, você precisará executar uma pesquisa JNDI e armazenar a instância EJB retornada no HttpSessionobjeto para atividades futuras. Algo parecido:

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
fonte
Obrigado pelo esclarecimento. Quando eu uso um programa de linha de comando independente para o cliente, é óbvio ver a diferença.
Stanley Kelly,
obrigado por seus comentários, eles são mais esclarecedores. primeiro você fornece a definição abstrata, depois especifica alguns casos de uso para cada situação e, a seguir, aponta algumas armadilhas. Grande +1
arthur
A parte de evitar injeção sai para o EJB 3.1 também?
jacktrades de
7
@Pascal se "Stateful Session Beans (SFSB) são dedicados a um cliente por toda a sua vida", ou seja, essa capacidade é construída no SFSB, então por que precisa armazená-los no objeto HttpSession?
user1169587
2
Por que precisamos manter o bean com estado na sessão se ele já está 'em sessão'? Dessa forma, podemos fazer com que cada objeto seja colocado em sessão. Explique por
favor
18

Stateless e stateful neste contexto não significam exatamente o que você poderia esperar.

Statefulness com EJBs refere-se ao que chamo de estado de conversação . O exemplo clássico é uma reserva de voo. Se consistir em três etapas:

  • Reserve assento
  • Cobrar cartão de crédito
  • Bilhete de emissão

Imagine que cada um deles é uma chamada de método para um bean de sessão. Um bean de sessão com preservação de estado pode manter esse tipo de conversação para que se lembre do que acontece entre as chamadas.

Os beans de sessão sem estado não têm essa capacidade para o estado conversacional.

Variáveis ​​globais dentro de um bean de sessão (sem estado ou com estado) são algo totalmente diferente. Os beans de sessão stateful terão um pool de beans criado (já que um bean só pode ser usado em uma conversa por vez) enquanto os beans de sessão stateless geralmente terão apenas uma instância, o que fará com que a variável global funcione, mas eu não acho isso é necessariamente garantido.

cletus
fonte
5

As principais diferenças entre os dois tipos principais de beans de sessão são:

Feijão sem estado

  1. Beans de sessão sem estado são aqueles que não têm estado de conversação com o cliente que chamou seus métodos. Por esta razão, eles podem criar um conjunto de objetos que podem ser usados ​​para interagir com vários clientes.
  2. Os beans sem estado com bom desempenho são melhores, pois não têm estados por cliente.
  3. Eles podem lidar com várias solicitações de vários clientes em paralelo.

Stateful Beans

  1. Os beans de sessão com estado podem manter o estado de conversação com vários clientes ao mesmo tempo e a tarefa não é compartilhada entre os clientes.
  2. Após a conclusão da sessão, o estado não é retido.
  3. O contêiner pode serializar e armazenar o estado como um estado obsoleto para uso futuro. Isso é feito para economizar recursos do servidor de aplicativos e para suportar falhas de bean.
Pritam Banerjee
fonte
4

Isso acontece porque o contêiner possui apenas uma instância de bean no pool que está sendo reutilizada para todas as chamadas. Se você executar os clientes em paralelo, verá um resultado diferente porque o contêiner criará mais instâncias de bean no pool.

Neyma
fonte
4

Tem boas respostas. Eu gostaria de adicionar uma pequena resposta. O Bean sem estado não deve ser usado para armazenar dados do cliente. Deve ser usado para "modelar ações ou processos que podem ser feitos de uma só vez".

Malatesh
fonte
4

Boa pergunta,

tente este código (altere 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

ZURA Tikaradze
fonte
1
Sim, é isso e funciona! Explicação muito fácil, obrigado!
Nesquik27