Bezstanowe i stanowe korporacyjne fasole Java

94

Przechodzę przez samouczek Java EE 6 i próbuję zrozumieć różnicę między fasolami sesji bezstanowych i stanowymi. Jeśli bezstanowe ziarna sesji nie zachowują swojego stanu między wywołaniami metod, dlaczego mój program zachowuje się tak, jak jest?

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

Klient

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

}

Spodziewałem się, że getNumber zwróci 0 za każdym razem, ale zwraca 1, a przeładowania serwletu w mojej przeglądarce zwiększają go bardziej. Problem polega na tym, że rozumiem, jak działają bezstanowe komponenty bean sesji, a nie oczywiście z bibliotekami lub serwerem aplikacji. Czy ktoś może mi podać prosty przykład hello world typu bean bezstanowej sesji, który zachowuje się inaczej po zmianie na stanowy?

Stanley Kelly
źródło
6
Powiązane: stackoverflow.com/questions/8887140/… Ta odpowiedź może być prostsza do zrozumienia. Należy zauważyć, że serwlety są zasadniczo ograniczone do zakresu aplikacji (istnieje tylko jedna instancja serwletu obejmująca całą aplikację, która jest współużytkowana / ponownie wykorzystywana we wszystkich żądaniach / sesjach HTTP.
BalusC,
cześć, najpierw dokonujesz inkrementacji, a potem otrzymujesz wartość .... więc nie możesz oczekiwać wartości 0.
rzur2004
Chcę tylko podziękować za to pytanie, to rozwiązuje mój problem w tej chwili. Nie mogłem prosić o to lepiej
kholofelo Maloma

Odpowiedzi:

94

Istotną różnicą nie są prywatne zmienne składowe, ale powiązanie stanu z konkretnym użytkownikiem (pomyśl „koszyk”).

Stanowy element stanowego komponentu bean sesji jest podobny do sesji w serwletach. Stanowe komponenty bean sesji umożliwiają aplikacji zachowanie tej sesji, nawet jeśli nie ma klienta WWW. Gdy serwer aplikacji pobiera bezstanowy komponent bean sesji z puli obiektów, wie, że można go użyć do spełnienia DOWOLNEGO żądania, ponieważ nie jest on skojarzony z określonym użytkownikiem.

Stanowy bean sesji musi zostać przekazany użytkownikowi, który go dostał, ponieważ informacje o jego koszyku powinny być znane tylko jemu. Serwer aplikacji zapewnia, że ​​tak jest. Wyobraź sobie, jak popularna byłaby Twoja aplikacja, gdybyś mógł zacząć robić zakupy, a serwer aplikacji przekazał mi Twój stanowy bean sesji, kiedy się pojawiłem!

Więc twój prywatny członek danych jest rzeczywiście „stan”, ale nie jest to „koszyk”. Spróbuj powtórzyć (bardzo dobry) przykład, tak aby zmienna inkrementowana była powiązana z konkretnym użytkownikiem. Zwiększ go, utwórz nowego użytkownika i sprawdź, czy nadal widzą zwiększoną wartość. Jeśli zrobisz to poprawnie, każdy użytkownik powinien zobaczyć tylko swoją wersję licznika.

duffymo
źródło
Czy możesz podać w komentarzu jednoznaczną odpowiedź? Dlaczego fasola bezpaństwowa w tym przykładzie zawsze ma wartość i za każdym razem ją zwiększa? Ponieważ jest tylko jeden użytkownik?
arjacsoh
2
Licznik będzie się zwiększał niezależnie od liczby użytkowników. Więc jeśli użytkownik1 wejdzie i zwiększy licznik do 1, a jednocześnie użytkownik2 wejdzie i zwiększy go, wartość wyniesie 2. W rzeczywistości powinno pokazać, że użytkownik1 ma 1, a użytkownik2 ma 1 (jeśli to właśnie zamierzasz zrobić. Koszyk przykład jak wyżej).
Krishna,
138

Bezstanowe komponenty bean sesji (SLSB) niepowiązane z jednym klientem i nie ma gwarancji, że jeden klient otrzyma tę samą instancję przy każdym wywołaniu metody (niektóre kontenery mogą tworzyć i niszczyć komponenty bean przy każdej sesji wywołania metody, jest to decyzja specyficzna dla implementacji , ale instancje są zwykle łączone - i nie wspominam o środowiskach klastrowych). Innymi słowy, chociaż bezstanowe komponenty bean mogą mieć zmienne instancji, pola te nie są specyficzne dla jednego klienta, więc nie należy na nich polegać między zdalnymi wywołaniami.

W przeciwieństwie do tego Stateful Session Beans (SFSB) są dedykowane jednemu klientowi przez całe życie, nie ma zamiany ani gromadzenia instancji (może zostać usunięty z pamięci po pasywacji, aby zaoszczędzić zasoby, ale to już inna historia) i utrzymać stan konwersacji . Oznacza to, że zmienne instancji komponentu bean mogą przechowywać dane względem klienta między wywołaniami metod. Dzięki temu możliwe są współzależne wywołania metod (zmiany dokonane przez jedną metodę wpływają na kolejne wywołania metod). Procesy wieloetapowe (rejestracja, koszyk, proces rezerwacji ...) to typowe przypadki użycia SFSB.

Jeszcze jedna rzecz. Jeśli używasz SFSB, musisz unikać wstrzykiwania ich do klas, które są z natury wielowątkowe, takich jak serwlety i komponenty bean zarządzane przez JSF (nie chcesz, aby były one udostępniane wszystkim klientom). Jeśli chcesz używać SFSB w swojej aplikacji internetowej, musisz przeprowadzić wyszukiwanie JNDI i zapisać zwróconą instancję EJB w HttpSessionobiekcie na potrzeby przyszłych działań. Coś w tym stylu:

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
źródło
Dzięki za wyjaśnienie. Kiedy używam samodzielnego programu wiersza poleceń dla klienta, widać różnicę.
Stanley kelly
dzięki za komentarze, są bardziej pouczające. najpierw podaj abstrakcyjną definicję, następnie określ kilka przypadków użycia dla każdej sytuacji, a następnie wskaż pułapki. Świetne +1
artur
Czy część unikająca wtrysku gaśnie również w przypadku EJB 3.1?
jacktrades
7
@Pascal, jeśli „Stateful Session Beans (SFSB) są dedykowane jednemu klientowi przez całe życie”, to znaczy, że ta zdolność jest wbudowana w SFSB, to po co przechowywać je w obiekcie HttpSession?
user1169587
2
Dlaczego potrzebujemy utrzymywać fasolkę stanową w sesji, jeśli jest już „sesyjna”? W ten sposób możemy ustawić sesję każdego obiektu. Wyjaśnij
proszę
18

Bezpaństwowość i stan w tym kontekście nie oznaczają do końca tego, czego można by się spodziewać.

Stanowość z EJB odnosi się do tego, co nazywam stanem konwersacyjnym . Klasycznym przykładem jest rezerwacja lotu. Jeśli składa się z trzech kroków:

  • Zarezerwuj miejsce
  • Obciąż kartę kredytową
  • Wydanie biletu

Wyobraź sobie, że każdy z nich jest wywołaniem metody do komponentu bean sesji. Stanowy bean sesji może podtrzymywać tego rodzaju konwersację, aby pamiętać, co dzieje się między połączeniami.

Bezstanowe ziarna sesji nie mają takiej zdolności do tworzenia stanu konwersacyjnego.

Zmienne globalne wewnątrz komponentu bean sesji (bezstanowe lub stanowe) to coś zupełnie innego. Stanowe ziarna sesji będą miały utworzoną pulę fasoli (ponieważ fasola może być używana tylko w jednej rozmowie naraz), podczas gdy bezstanowe ziarna sesji będą często miały tylko jedną instancję, co spowoduje, że zmienna globalna będzie działać, ale nie sądzę jest to koniecznie zagwarantowane.

cletus
źródło
5

Dobre pytanie,

wypróbuj ten kod (zmień 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
źródło
1
Tak, to jest to i działa! Bardzo proste wyjaśnienie, dzięki!
Nesquik
5

Główne różnice między dwoma głównymi typami ziaren sesyjnych to:

Fasola bezpaństwowa

  1. Bezstanowe fasolki sesji to te, które nie mają stanu konwersacji z klientem, który wywołał jego metody. Z tego powodu mogą tworzyć pulę obiektów, których można używać do interakcji z wieloma klientami.
  2. Bezstanowe fasole pod względem wydajności są lepsze, ponieważ nie mają stanów na klienta.
  3. Mogą równolegle obsługiwać wiele żądań od wielu klientów.

Stateful Beans

  1. Stanowe komponenty bean sesji mogą utrzymywać stan konwersacji z wieloma klientami jednocześnie, a zadanie nie jest współdzielone między nimi.
  2. Po zakończeniu sesji stan nie jest zachowywany.
  3. Kontener może serializować i przechowywać stan jako nieaktualny stan do wykorzystania w przyszłości. Ma to na celu zaoszczędzenie zasobów serwera aplikacji i obsługę awarii komponentów bean.
Pritam Banerjee
źródło
4

Dzieje się tak, ponieważ kontener ma tylko jedną instancję komponentu bean w puli, która jest ponownie używana dla wszystkich wywołań. Jeśli uruchomisz klientów równolegle, zobaczysz inny wynik, ponieważ kontener utworzy więcej instancji bean w puli.

Neyma
źródło
4

Ma dobre odpowiedzi. Chciałbym dodać małą odpowiedź. Bezstanowy komponent Bean nie powinien przechowywać żadnych danych klienta. Powinien służyć do „modelowania działań lub procesów, które można wykonać za jednym zamachem”.

Malatesh
źródło