Корпоративные компоненты Java без сохранения состояния и с отслеживанием состояния

94

Я просматриваю руководство по 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, и перезагрузки сервлета в моем браузере увеличивают его еще больше. Проблема заключается в моем понимании того, как работают сессионные компоненты без сохранения состояния, а не в библиотеках или сервере приложений, конечно. Может ли кто-нибудь дать мне простой пример типа hello world сеансового bean-компонента без сохранения состояния, который ведет себя по-другому, когда вы меняете его на состояние с сохранением состояния?

Стэнли Келли
источник
6
Связано: stackoverflow.com/questions/8887140/… Этот ответ, возможно, проще для понимания. Обратите внимание, что сервлеты в основном привязаны к области приложения (есть только 1 экземпляр сервлета для всего приложения, который используется совместно / повторно во всех HTTP-запросах / сеансах.
BalusC
привет, вы сначала увеличиваете, а затем получаете значение .... так что вы не можете ожидать значение 0.
rzur2004
Я просто хочу поблагодарить вас за этот вопрос, на данный момент это решает мою проблему. Я не мог бы и лучше
спросить

Ответы:

94

Важное различие заключается не в частных переменных-членах, а в связывании состояния с конкретным пользователем (подумайте о «корзине покупок»).

Фрагмент сессионного компонента с отслеживанием состояния похож на сеанс в сервлетах. Сессионные компоненты с отслеживанием состояния позволяют вашему приложению продолжать этот сеанс, даже если нет веб-клиента. Когда сервер приложений извлекает сеансовый компонент без сохранения состояния из пула объектов, он знает, что его можно использовать для удовлетворения ЛЮБОГО запроса, поскольку он не связан с конкретным пользователем.

Сессионный компонент с сохранением состояния должен быть выдан пользователю, который его получил, потому что информация об их корзине покупок должна быть известна только им. Сервер приложений гарантирует, что это так. Представьте, насколько популярным было бы ваше приложение, если бы вы могли начать делать покупки, а затем сервер приложений предоставил мне ваш сессионный компонент с отслеживанием состояния, когда я пришел!

Таким образом, ваш частный элемент данных действительно является «государственным», но это не «корзина для покупок». Попробуйте повторить свой (очень хороший) пример, чтобы увеличиваемая переменная была связана с конкретным пользователем. Увеличьте его, создайте нового пользователя и посмотрите, могут ли они по-прежнему видеть увеличенное значение. Если все сделано правильно, каждый пользователь должен видеть только свою версию счетчика.

Duffymo
источник
Можете ли вы дать в комментарии четкий ответ? Почему в этом примере bean-компонент без сохранения состояния всегда сохраняет значение и каждый раз увеличивает его? Потому что есть только один пользователь?
arjacsoh 03
2
Счетчик будет увеличиваться независимо от количества пользователей. Таким образом, если заходит user1 и увеличивает счетчик до 1, и одновременно заходит user2 и увеличивает его, значение будет 2. Фактически оно должно показать, что user1 имеет 1, а user2 - 1 (если это то, что вы собираетесь делать. Корзина пример, как указано выше).
Кришна
138

Сессионные компоненты без сохранения состояния (SLSB) не привязаны к одному клиенту, и нет гарантии, что один клиент получит один и тот же экземпляр при каждом вызове метода (некоторые контейнеры могут создавать и уничтожать компоненты при каждом сеансе вызова метода, это решение зависит от реализации. , но экземпляры обычно объединяются в пулы - и я не упоминаю кластерные среды). Другими словами, хотя bean-компоненты без состояния могут иметь переменные экземпляра, эти поля не относятся к одному клиенту, поэтому не полагайтесь на них между удаленными вызовами.

Напротив, сеансовые компоненты с отслеживанием состояния (SFSB) предназначены для одного клиента на протяжении всей их жизни, нет обмена или объединения экземпляров (они могут быть удалены из памяти после пассивации для экономии ресурсов, но это уже другая история) и поддерживают диалоговое состояние . Это означает, что переменные экземпляра bean-компонента могут хранить данные относительно клиента между вызовами методов. И это позволяет иметь взаимозависимые вызовы методов (изменения, внесенные одним методом, влияют на последующие вызовы методов). Многоступенчатые процессы (процесс регистрации, корзина покупок, процесс бронирования ...) являются типичными вариантами использования 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, если «Сессионные компоненты с отслеживанием состояния (SFSB) предназначены для одного клиента на всю жизнь», то есть эта возможность встроена в SFSB, то зачем хранить их в объекте HttpSession?
user1169587 04
2
Зачем нам нужно держать bean-компонент с отслеживанием состояния в сеансе, если он уже "сеансирован"? Таким образом мы можем сделать каждый объект сессионным. Объясните, пожалуйста
Георгий Гобозов
18

Без сохранения состояния и с сохранением состояния в этом контексте не совсем то, чего вы могли ожидать.

Состояние с EJB относится к тому, что я называю диалоговым состоянием . Классический пример - бронирование авиабилетов. Если он состоит из трех шагов:

  • Резервное место
  • Пополнить счет кредитной картой
  • Выдать билет

Представьте, что каждый из них является вызовом метода сеансового компонента. Сессионный компонент с отслеживанием состояния может поддерживать такой диалог, чтобы запоминать, что происходит между вызовами.

Сессионные компоненты без сохранения состояния не имеют такой возможности для разговорного состояния.

Глобальные переменные внутри сессионного компонента (без состояния или с сохранением состояния) - это нечто совершенно иное. Сессионные бины с сохранением состояния будут иметь пул бинов (поскольку бин может использоваться только в одном диалоге за раз), тогда как сеансные бины без сохранения состояния часто будут иметь только один экземпляр, что заставит глобальную переменную работать, но я не думаю это обязательно гарантировано.

Cletus
источник
5

Хороший вопрос,

попробуйте этот код (измените 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 - @ без сохранения состояния

http: // локальный: 8080 / MYServletDemo / ServletClient

1

http: // локальный: 8080 / MYServletDemo / ServletClient

2

http: // локальный: 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: // локальный: 8080 / MYServletDemo / newServletClient

1

http: // локальный: 8080 / MYServletDemo / ServletClient

3

ЗУРА Тикарадзе
источник
1
Да, вот и все, и это работает! Очень простое объяснение, спасибо!
Nesquik27
5

Основные различия между двумя основными типами сессионных компонентов:

Бобы без сохранения состояния

  1. Сессионные компоненты без сохранения состояния - это те компоненты, которые не имеют состояния разговора с клиентом, вызвавшим его методы. По этой причине они могут создать пул объектов, который можно использовать для взаимодействия с несколькими клиентами.
  2. Бины без сохранения состояния с точки зрения производительности лучше, поскольку у них нет состояний для каждого клиента.
  3. Они могут обрабатывать несколько запросов от нескольких клиентов параллельно.

Бобы с отслеживанием состояния

  1. Сессионные компоненты с отслеживанием состояния могут поддерживать состояние разговора с несколькими клиентами одновременно, и задача не распределяется между клиентами.
  2. После завершения сеанса состояние не сохраняется.
  3. Контейнер может сериализовать и сохранить состояние как устаревшее для использования в будущем. Это сделано для экономии ресурсов сервера приложений и поддержки сбоев bean-компонентов.
Притам Банерджи
источник
4

Это происходит потому, что контейнер имеет только один экземпляр компонента в пуле, который повторно используется для всех вызовов. Если вы запустите клиентов параллельно, вы увидите другой результат, потому что контейнер создаст больше экземпляров bean-компонентов в пуле.

Нейма
источник
4

На него есть хорошие ответы. Хочу добавить небольшой ответ. Бин без сохранения состояния не должен использоваться для хранения каких-либо клиентских данных. Его следует использовать для «моделирования действий или процессов, которые можно выполнить за один раз».

Малатеш
источник