Безгранні та державні корпоративні Java-компоненти


93

Я переглядаю підручник з Java EE 6 і намагаюся зрозуміти різницю між компонентами сеансів без стану та станом. Якщо сеансові компоненти без стану не зберігають свого стану між викликами методів, чому моя програма діє так, як є?

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

Клієнт

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

}

Я очікував, що getNumber поверне 0 кожного разу, але він повертає 1, і перезавантаження сервлета в моєму браузері збільшує його більше. Проблема полягає в моєму розумінні того, як працюють компоненти сеансів без стану, а не в бібліотеках або на сервері додатків, звичайно. Чи може хто-небудь навести мені простий приклад типу привітний бін сеансу без громадянства, який поводиться інакше, коли ви змінюєте його на статус із станом?


6
Пов’язане: stackoverflow.com/questions/8887140/… Цю відповідь, можливо, простіше зрозуміти. Зверніть увагу, що сервлети в основному мають область застосування (існує лише 1 екземпляр сервлета, який спільно використовується / використовується повторно для всіх запитів / сеансів HTTP.
BalusC,

привіт, ти спочатку зробиш збільшення, а потім отримаєш значення .... так що ти не можеш очікувати значення 0.
rzur2004

Я просто хочу подякувати вам за запитання, це стосується моєї проблеми на даний момент. Я не міг би просити цього краще
холофело Малома

Відповіді:


93

Важливою відмінністю є не змінні приватних членів, а асоціювання стану з певним користувачем (подумайте "кошик покупок").

Штатний фрагмент компонента сеансу, що містить статус, подібний до сеансу в сервлетах. Файли сеансів із підтримкою стану дозволяють додатку продовжувати цей сеанс, навіть якщо веб-клієнт відсутній. Коли сервер додатків виймає з пулу об’єктів компонент сеансу без стану, він знає, що його можна використовувати для задоволення БУДЬ-ЯКОГО запиту, оскільки він не пов’язаний з певним користувачем.

Сеансовий компонент, що має статус сеансу, повинен бути виданий користувачеві, який його отримав, в першу чергу, оскільки інформація про його кошик для покупок повинна бути відома лише їм. Сервер додатків гарантує, що це так. Уявіть, якою популярністю користувався б ваш додаток, якби ви могли почати робити покупки, а потім сервер додатків передав бит вашого сесійного стану мені, коли я прийшов!

Отже, ваш учасник приватних даних справді є "державним", але це не "кошик для покупок". Спробуйте переробити свій (дуже хороший) приклад, щоб зробити так, щоб збільшена змінна асоціювалася з певним користувачем. Збільште його, створіть нового користувача і перевірте, чи зможуть вони бачити збільшене значення. Якщо все зроблено правильно, кожен користувач повинен бачити лише свою версію лічильника.


Чи можете ви дати в коментарі явну відповідь? Чому в цьому прикладі компонент без громадянства завжди тримає значення і щоразу збільшує його? Тому що є лише один користувач?
arjacsoh

2
Лічильник буде збільшуватися незалежно від кількості користувачів. Отже, якщо користувач1 заходить і збільшує лічильник до 1, а одночасно заходить користувач2 і збільшує його, значення буде 2. Це фактично повинно показувати, що користувач1 має 1, а користувач2 має 1 (якщо це те, що ви збираєтеся зробити. Кошик приклад, як зазначено вище).
Крішна

137

Файли сеансу без збереження стану (SLSB) не прив’язані до одного клієнта, і немає гарантії, що один клієнт отримає однаковий екземпляр із кожним викликом методу (деякі контейнери можуть створювати та знищувати компоненти з кожним сеансом виклику методу, це рішення, яке залежить від реалізації. , але екземпляри зазвичай об’єднуються - і я не згадую кластерні середовища). Іншими словами, хоча компоненти без стану можуть мати змінні екземпляра, ці поля не є специфічними для одного клієнта, тому не покладайтесь на них між віддаленими викликами.

На відміну від цього, Stateful Session Beans (SFSB) присвячені одному клієнтові протягом усього життя, немає обміну або об'єднання екземплярів (його можна витіснити з пам'яті після пасивації, щоб заощадити ресурси, але це вже інша історія) та підтримувати стан розмови . Це означає, що змінні екземпляра компонента можуть зберігати дані щодо клієнта між викликами методів. І це дозволяє мати взаємозалежні виклики методів (зміни, внесені одним методом, впливають на наступні виклики методів). Багатоступеневі процеси (процес реєстрації, кошик для покупок, процес бронювання ...) - типові випадки використання для SFSB.

І ще одна річ. Якщо ви використовуєте SFSB, то вам слід уникати їх ін'єкцій у класи, які мають багатопотоковість, такі як сервлети та керовані файли JSF (ви не хочете, щоб він був спільним для всіх клієнтів). Якщо ви хочете використовувати SFSB у своїй веб-програмі, вам потрібно виконати пошук JNDI та зберегти повернутий екземпляр EJB в HttpSessionоб’єкт для подальшої діяльності. Щось схоже:

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

Дякую за роз'яснення. Коли я використовую автономну програму командного рядка для клієнта, очевидно, щоб побачити різницю.
Стенлі Келлі

дякую за ваші коментарі, вони більш просвітливі. спочатку дайте абстрактне визначення, потім вкажіть деякі варіанти використання для кожної ситуації, а потім вкажіть на деякі підводні камені. Чудовий +1
Артур

Чи виключається частина впорскування також для EJB 3.1?
jacktrades

7
@Pascal, якщо "Stateful Session Beans (SFSB) присвячені одному клієнту протягом усього життя", тобто ця здатність вбудована в SFSB, то навіщо їх зберігати на об'єкті HttpSession?
user1169587 04.03.13

2
Навіщо нам потрібно утримувати фасолевий боб на сесії, якщо він уже "сесонований"? Таким чином ми можемо зробити кожен об’єкт сесонованим. Поясніть pls
Георгій Гобозов

18

У цьому контексті особи без громадянства та статуси не означають зовсім того, на що можна очікувати.

Державність при EJB стосується того, що я називаю розмовним станом . Класичний приклад - бронювання авіарейсів. Якщо він складається з трьох етапів:

  • Резервне місце
  • Стягніть кредитну картку
  • Випуск квитка

Уявіть, що кожен із них є викликом методу до сеансового компонента. Сеансовий компонент із підтримкою стану може підтримувати такий тип розмови, щоб пам’ятати, що відбувається між дзвінками.

Сеанси без громадянства не мають такої здатності до розмовного стану.

Глобальні змінні всередині компонента сеансу (без стану чи стану) - це щось зовсім інше. Файли сеансів із увімкненням стану матимуть створений пул компонентів (оскільки компонент bean можна використовувати лише в одній розмові за раз), тоді як компоненти сеансів без громадянства часто матимуть лише один екземпляр, що змусить глобальну змінну працювати, але я не думаю це обов'язково гарантовано.


5

Основні відмінності між двома основними типами сеансових зерен:

Квасоля без громадянства

  1. Файли сеансу без громадянства - це ті, у яких немає стану розмови з клієнтом, який назвав свої методи. З цієї причини вони можуть створити пул об'єктів, які можна використовувати для взаємодії з кількома клієнтами.
  2. Ефективні компоненти без громадянства є кращими, оскільки вони не мають станів на одного клієнта.
  3. Вони можуть обробляти кілька запитів від декількох клієнтів паралельно.

Державна квасоля

  1. Файли сеансів із увімкненням стану можуть підтримувати стан розмови з кількома клієнтами одночасно, і завдання не розподіляється між клієнтами.
  2. Після завершення сесії стан не зберігається.
  3. Контейнер може серіалізувати і зберігати стан як застарілий стан для подальшого використання. Це робиться для економії ресурсів сервера додатків та підтримки несправностей компонента.

4

Це трапляється, оскільки контейнер має лише один екземпляр компонента в пулі, який використовується повторно для всіх викликів. Якщо паралельно запускати клієнтів, ви побачите інший результат, оскільки контейнер створить більше екземплярів компонентів у пулі.


4

Він має добрі відповіді. Я хотів би додати невелику відповідь. Бін без громадянства не повинен використовуватися для зберігання будь-яких даних клієнта. Його слід використовувати для "моделювання дій або процесів, які можна зробити за один постріл".


4

Гарне питання,

спробуйте цей код (змініть 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++;
    }
}

Сервлет_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());
        }

    }

Сервлет_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: // локальний: 8080 / MYServletDemo / ServletClient

1

HTTP: // локальний: 8080 / MYServletDemo / ServletClient

2

http: // localhost: 8080 / MYServletDemo_war_exploded / newServletClient

3

HTTP: // локальний: 8080 / MYServletDemo / ServletClient

4

case: MyBean - @ Stateful

HTTP: // локальний: 8080 / MYServletDemo / ServletClient

1

HTTP: // локальний: 8080 / MYServletDemo / ServletClient

2

http: // localhost: 8080 / MYServletDemo / newServletClient

1

HTTP: // локальний: 8080 / MYServletDemo / ServletClient

3


1
Так, це все, і це працює! Дуже просте пояснення, дякую!
Nesquik27,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.