Java Beans aziendali stateless e stateful


93

Sto esaminando il tutorial Java EE 6 e sto cercando di capire la differenza tra i bean di sessione senza stato e con stato. Se i bean di sessione senza stato non mantengono il loro stato tra le chiamate di metodo, perché il mio programma si comporta in questo modo?

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

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

}

Mi aspettavo che getNumber restituisse 0 ogni volta, ma restituisce 1 e i ricaricamenti del servlet nel mio browser lo aumentano di più. Il problema è con la mia comprensione di come funzionano i bean di sessione senza stato e non con le librerie o il server delle applicazioni, ovviamente. Qualcuno può darmi un semplice esempio di tipo Hello World di un bean di sessione senza stato che si comporta in modo diverso quando lo si cambia in stateful?


6
Correlato: stackoverflow.com/questions/8887140/… Questa risposta è forse più semplice da capire. Si noti che i servlet sono fondamentalmente nell'ambito dell'applicazione (esiste solo 1 istanza servlet in tutta l'applicazione che viene condivisa / riutilizzata tra tutte le richieste / sessioni HTTP.
BalusC

ciao, prima incrementa e poi ottieni il valore .... quindi non puoi aspettarti un valore di 0.
rzur2004

Voglio solo ringraziarti per averlo chiesto, risolve il mio problema al momento. Non avrei potuto chiedere di meglio
kholofelo Maloma

Risposte:


93

La differenza importante non sono le variabili membro private, ma l'associazione dello stato a un particolare utente (si pensi a "carrello della spesa").

La parte stateful del bean di sessione stateful è come la sessione nei servlet. I bean di sessione con stato consentono alla tua app di mantenere quella sessione anche se non è presente un client Web. Quando l'app server recupera un bean di sessione senza stato dal pool di oggetti, sa che può essere utilizzato per soddisfare QUALSIASI richiesta, poiché non è associato a un particolare utente.

Un bean di sessione con stato deve essere distribuito all'utente che lo ha ottenuto in primo luogo, poiché le informazioni sul carrello degli acquisti dovrebbero essere note solo a lui. L'app server garantisce che sia così. Immagina quanto sarebbe popolare la tua app se potessi iniziare a fare acquisti e poi il server dell'app mi ha dato il tuo bean di sessione con stato quando sono arrivato!

Quindi il membro dei dati personali è effettivamente "stato", ma non è "carrello della spesa". Prova a ripetere il tuo (molto buono) esempio per fare in modo che la variabile incrementata sia associata a un particolare utente. Incrementalo, crea un nuovo utente e verifica se possono ancora vedere il valore incrementato. Se fatto correttamente, ogni utente dovrebbe vedere solo la propria versione del contatore.


Puoi fornire in un commento una risposta esplicita? Perché il bean senza stato in questo esempio mantiene sempre il valore e lo aumenta ogni volta? Perché c'è un solo utente?
arjacsoh

2
Il contatore aumenterà indipendentemente dal numero di utenti. Quindi, se l'utente1 entra e incrementa il contatore a 1 e contemporaneamente l'utente2 arriva e lo incrementa, il valore sarà 2. In realtà dovrebbe mostrare che utente1 ha 1 e utente2 ha 1 (se è quello che intendi fare. esempio come sopra).
Krishna

137

Gli Stateless Session Beans (SLSB) non sono legati a un client e non vi è alcuna garanzia che un client ottenga la stessa istanza con ogni invocazione di metodo (alcuni contenitori possono creare e distruggere i bean con ogni sessione di invocazione del metodo, questa è una decisione specifica dell'implementazione , ma le istanze sono in genere raggruppate e non menziono gli ambienti in cluster). In altre parole, sebbene i bean senza stato possano avere variabili di istanza, questi campi non sono specifici per un client, quindi non fare affidamento su di essi tra le chiamate remote.

Al contrario, gli Stateful Session Beans (SFSB) sono dedicati a un cliente per tutta la vita, non c'è scambio o raggruppamento di istanze (può essere rimosso dalla memoria dopo la passivazione per risparmiare risorse, ma questa è un'altra storia) e mantenere lo stato di conversazione . Ciò significa che le variabili di istanza del bean possono conservare i dati relativi al client tra le chiamate di metodo. E questo rende possibile avere chiamate di metodo interdipendenti (le modifiche apportate da un metodo influenzano le chiamate di metodo successive). I processi in più fasi (un processo di registrazione, un carrello della spesa, un processo di prenotazione ...) sono casi d'uso tipici per SFSB.

Un'altra cosa. Se si utilizza SFSB, è necessario evitare di iniettarli in classi di natura multithread, come Servlet e bean gestiti JSF (non si desidera che siano condivisi da tutti i client). Se si desidera utilizzare SFSB nella propria applicazione Web, è necessario eseguire una ricerca JNDI e memorizzare l'istanza EJB restituita HttpSessionnell'oggetto per attività future. Qualcosa del genere:

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

Grazie per aver chiarito. Quando utilizzo un programma a riga di comando autonomo per il client, è ovvio vedere la differenza.
Stanley kelly

grazie per i vostri commenti, sono più illuminanti. prima dai la definizione astratta, quindi specifica alcuni casi d'uso per ciascuna situazione e poi evidenzia alcune insidie. Ottimo +1
Arthur

La parte per evitare l'iniezione si spegne anche per EJB 3.1?
jacktrades

7
@Pascal se "Stateful Session Beans (SFSB) sono dedicati a un client per tutta la loro vita", ovvero questa capacità è costruita in SFSB, allora perché è necessario memorizzarli sull'oggetto HttpSession?
user1169587

2
Perché abbiamo bisogno di tenere il bean stateful nella sessione se è già "in sessione"? In questo modo possiamo mettere in sessione ogni oggetto. Spiega
Georgy Gobozov

18

Apolidi e stateful in questo contesto non significano esattamente ciò che potresti aspettarti.

Statefulness con EJB si riferisce a quello che chiamo stato di conversazione . L'esempio classico è la prenotazione di un volo. Se consiste in tre passaggi:

  • Riserva il posto
  • Carica carta di credito
  • Emissione del biglietto

Immagina che ognuno di questi sia una chiamata di metodo a un bean di sessione. Un bean di sessione con stato può mantenere questo tipo di conversazione in modo da ricordare cosa accade tra le chiamate.

I bean di sessione senza stato non hanno tale capacità per lo stato di conversazione.

Le variabili globali all'interno di un bean di sessione (stateless o stateful) sono qualcos'altro completamente. I bean di sessione con stato avranno un pool di bean creati (poiché un bean può essere utilizzato solo in una conversazione alla volta) mentre i bean di sessione senza stato avranno spesso solo un'istanza, il che farà funzionare la variabile globale, ma non credo questo è necessariamente garantito.


5

Le principali differenze tra i due principali tipi di bean di sessione sono:

Fagioli apolidi

  1. I bean di sessione stateless sono quelli che non hanno uno stato di conversazione con il client che ha chiamato i suoi metodi. Per questo motivo possono creare un pool di oggetti che possono essere utilizzati per interagire con più client.
  2. I bean senza stato dal punto di vista delle prestazioni sono migliori poiché non hanno stati per client.
  3. Possono gestire più richieste da più client in parallelo.

Stateful Beans

  1. I bean di sessione con stato possono mantenere lo stato di conversazione con più client contemporaneamente e l'attività non viene condivisa tra i client.
  2. Al termine della sessione, lo stato non viene mantenuto.
  3. Il contenitore può serializzare e archiviare lo stato come stato non aggiornato per un utilizzo futuro. Questa operazione viene eseguita per risparmiare risorse del server delle applicazioni e per supportare gli errori dei bean.

4

Questo accade perché il contenitore ha solo un'istanza di bean nel pool che viene riutilizzata per tutte le chiamate. Se esegui i client in parallelo, vedrai un risultato diverso perché il contenitore creerà più istanze di bean nel pool.


4

Ha buone risposte. Vorrei aggiungere una piccola risposta. Stateless Bean non deve essere utilizzato per conservare i dati del cliente. Dovrebbe essere utilizzato per "modellare azioni o processi che possono essere eseguiti in un colpo solo".


4

Buona domanda,

prova questo codice (cambia 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


1
Sì, è tutto e funziona! Spiegazione molto semplice, grazie!
Nesquik27
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.