Zustandslose und Stateful Enterprise Java Beans

93

Ich gehe das Java EE 6-Tutorial durch und versuche, den Unterschied zwischen zustandslosen und zustandsbehafteten Session-Beans zu verstehen. Wenn zustandslose Session-Beans ihren Status zwischen Methodenaufrufen nicht beibehalten, warum verhält sich mein Programm dann so, wie es ist?

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

Der Kunde

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

}

Ich hatte erwartet, dass getNumber jedes Mal 0 zurückgibt, aber es gibt 1 zurück, und das Neuladen des Servlets in meinem Browser erhöht es weiter. Das Problem liegt in meinem Verständnis der Funktionsweise zustandsloser Session Beans und natürlich nicht in den Bibliotheken oder dem Anwendungsserver. Kann mir jemand ein einfaches Beispiel für eine zustandslose Session-Bean geben, die sich anders verhält, wenn Sie sie in stateful ändern?

Stanley Kelly
quelle
6
Siehe auch : stackoverflow.com/questions/8887140/… Diese Antwort ist möglicherweise einfacher zu verstehen. Beachten Sie, dass Servlets grundsätzlich anwendungsbezogen sind (es gibt nur eine anwendungsweite Servlet-Instanz, die für alle HTTP-Anforderungen / Sitzungen freigegeben / wiederverwendet wird.
BalusC
Hallo, Sie
erhöhen
Ich möchte mich nur bei Ihnen dafür bedanken, dass Sie dies gefragt haben. Es befasst sich im Moment mit meinem Problem. Ich hätte das nicht besser
fragen können

Antworten:

93

Der wichtige Unterschied besteht nicht in privaten Mitgliedsvariablen, sondern darin, den Status einem bestimmten Benutzer zuzuordnen (denken Sie an "Warenkorb").

Das Stateful-Stück Stateful Session Bean entspricht der Sitzung in Servlets. Mit Stateful Session Beans kann Ihre App diese Sitzung auch dann noch haben, wenn kein Webclient vorhanden ist. Wenn der App-Server eine zustandslose Session-Bean aus dem Objektpool abruft, weiß er, dass sie zur Erfüllung JEDER Anforderung verwendet werden kann, da sie keinem bestimmten Benutzer zugeordnet ist.

Eine Stateful-Session-Bean muss an den Benutzer verteilt werden, der sie ursprünglich erhalten hat, da ihre Warenkorbinformationen nur ihnen bekannt sein sollten. Der App-Server stellt dies sicher. Stellen Sie sich vor, wie beliebt Ihre App wäre, wenn Sie mit dem Einkaufen beginnen könnten, und dann gab mir der App-Server Ihre Stateful Session Bean, als ich mitkam!

Ihr privates Datenmitglied ist also zwar "Bundesstaat", aber kein "Einkaufswagen". Versuchen Sie, Ihr (sehr gutes) Beispiel so zu wiederholen, dass die inkrementierte Variable einem bestimmten Benutzer zugeordnet ist. Inkrementieren Sie es, erstellen Sie einen neuen Benutzer und prüfen Sie, ob er den inkrementierten Wert noch sehen kann. Bei korrekter Ausführung sollte jeder Benutzer nur seine Version des Zählers sehen.

Duffymo
quelle
Können Sie in einem Kommentar eine explizite Antwort geben? Warum hält die zustandslose Bohne in diesem Beispiel immer den Wert und erhöht ihn jedes Mal? Weil es nur einen Benutzer gibt?
Arjacsoh
2
Der Zähler wird unabhängig von der Anzahl der Benutzer erhöht. Wenn also Benutzer1 hereinkommt und den Zähler auf 1 erhöht und gleichzeitig Benutzer2 hereinkommt und ihn erhöht, ist der Wert 2. Es sollte tatsächlich zeigen, dass Benutzer1 1 und Benutzer2 1 hat (wenn Sie dies beabsichtigen. Warenkorb Beispiel wie oben).
Krishna
137

Stateless Session Beans (SLSB) sind nicht an einen Client gebunden , und es gibt keine Garantie dafür, dass ein Client bei jedem Methodenaufruf dieselbe Instanz erhält (einige Container können bei jeder Methodenaufrufsitzung Beans erstellen und zerstören. Dies ist eine implementierungsspezifische Entscheidung , aber Instanzen werden normalerweise gepoolt - und ich erwähne keine Clusterumgebungen). Mit anderen Worten, obwohl zustandslose Beans möglicherweise Instanzvariablen enthalten, sind diese Felder nicht für einen Client spezifisch. Verlassen Sie sich daher zwischen Remoteaufrufen nicht auf sie.

Im Gegensatz dazu Stateful Session Beans (SFSB) ist gewidmet , um einen Client für ihr ganzes Leben, gibt es keinen Austausch oder die Bündelung von Instanzen (aus dem Gedächtnis nach der Passivierung vertrieben werden kann , Ressourcen zu sparen , aber das ist ein andere Geschichte) und Konversations Zustand zu halten . Dies bedeutet, dass die Instanzvariablen der Bean zwischen Methodenaufrufen Daten relativ zum Client halten können. Dies ermöglicht voneinander abhängige Methodenaufrufe (Änderungen, die von einer Methode vorgenommen werden, wirken sich auf nachfolgende Methodenaufrufe aus). Mehrstufige Prozesse (ein Registrierungsprozess, ein Warenkorb, ein Buchungsprozess ...) sind typische Anwendungsfälle für SFSB.

Eine Sache noch. Wenn Sie SFSB verwenden, müssen Sie vermeiden, sie in Klassen mit mehreren Threads einzufügen, z. B. Servlets und JSF-verwaltete Beans (Sie möchten nicht, dass sie von allen Clients gemeinsam genutzt werden). Wenn Sie SFSB in Ihrer Webanwendung verwenden möchten, müssen Sie eine JNDI-Suche durchführen und die zurückgegebene EJB-Instanz HttpSessionfür zukünftige Aktivitäten im Objekt speichern . Sowas in der Art:

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
quelle
Danke für die Aufklärung. Wenn ich ein eigenständiges Befehlszeilenprogramm für den Client verwende, ist der Unterschied offensichtlich.
Stanley Kelly
Vielen Dank für Ihre Kommentare, sie sind aufschlussreicher. Zuerst geben Sie die abstrakte Definition an, geben dann einige Anwendungsfälle für jede Situation an und weisen dann auf einige Fallstricke hin. Großartig +1
Arthur
Geht der Teil zum Vermeiden des Einspritzens auch für EJB 3.1 aus?
Jacktrades
7
@Pascal Wenn "Stateful Session Beans (SFSB) ein Client für sein gesamtes Leben reserviert sind", dh diese Fähigkeit in SFSB integriert ist, warum müssen sie dann im HttpSession-Objekt gespeichert werden?
user1169587
2
Warum müssen wir Stateful Bean in der Sitzung halten, wenn sie bereits "Sitzung" hat? Auf diese Weise können wir jedes Objekt in eine Sitzung versetzen. Erklären Sie bitte
Georgy Gobozov
18

Staatenlos und zustandsbehaftet bedeuten in diesem Zusammenhang nicht ganz das, was Sie erwarten könnten.

Statefulness mit EJBs bezieht sich auf das, was ich Konversationsstatus nenne . Das klassische Beispiel ist eine Flugbuchung. Wenn es aus drei Schritten besteht:

  • Sitzplatz reservieren
  • Kreditkarte belasten
  • Ticket ausstellen

Stellen Sie sich vor, jeder dieser Methoden ist ein Methodenaufruf für eine Session-Bean. Eine Stateful Session Bean kann diese Art von Konversation aufrechterhalten, sodass sie sich merkt, was zwischen Anrufen passiert.

Zustandslose Session-Beans verfügen nicht über eine solche Kapazität für den Konversationsstatus.

Globale Variablen innerhalb einer Session Bean (zustandslos oder statusbehaftet) sind etwas ganz anderes. Bei Stateful Session Beans wird ein Pool von Beans erstellt (da eine Bean jeweils nur in einer Konversation verwendet werden kann), während zustandslose Sesion Beans häufig nur eine Instanz haben, wodurch die globale Variable funktioniert, aber ich denke nicht Dies ist unbedingt garantiert.

Cletus
quelle
5

Die großen Unterschiede zwischen den beiden Haupttypen von Session Beans sind:

Staatenlose Bohnen

  1. Stateless Session Beans sind solche, die keinen Konversationsstatus mit dem Client haben, der seine Methoden aufgerufen hat. Aus diesem Grund können sie einen Pool von Objekten erstellen, mit denen mit mehreren Clients interagiert werden kann .
  2. In Bezug auf die Leistung sind zustandslose Beans besser, da sie keine Status pro Client haben.
  3. Sie können mehrere Anforderungen von mehreren Clients gleichzeitig verarbeiten.

Stateful Beans

  1. Stateful Session Beans können den Konversationsstatus mit mehreren Clients gleichzeitig beibehalten, und die Aufgabe wird nicht von den Clients gemeinsam genutzt.
  2. Nach Abschluss der Sitzung bleibt der Status nicht erhalten .
  3. Der Container kann den Status serialisieren und als veralteten Status für die zukünftige Verwendung speichern . Dies geschieht, um Ressourcen des Anwendungsservers zu sparen und Bean-Fehler zu unterstützen.
Pritam Banerjee
quelle
4

Dies geschieht, weil der Container nur eine Bean-Instanz im Pool hat, die für alle Aufrufe wiederverwendet wird. Wenn Sie die Clients parallel ausführen, wird ein anderes Ergebnis angezeigt, da der Container mehr Bean-Instanzen im Pool erstellt.

Neyma
quelle
4

Es hat gute Antworten. Ich möchte eine kleine Antwort hinzufügen. Stateless Bean sollte nicht zum Speichern von Clientdaten verwendet werden. Es sollte verwendet werden, um "Aktionen oder Prozesse zu modellieren, die auf einen Schlag ausgeführt werden können".

Malatesh
quelle
4

Gute Frage,

Versuchen Sie diesen Code (ändern Sie 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());
    }

}

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

Fall: 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
quelle
1
Ja, das ist es und es funktioniert! Sehr einfache Erklärung, danke!
Nesquik27