Java-бины Enterprise с сохранением состояния и состоянием

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

7 ответов

Решение

Важным отличием являются не личные переменные-члены, а связь состояния с конкретным пользователем (например, "корзина").

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

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

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

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

Напротив, Stateful Session Beans (SFSB) выделены одному клиенту на протяжении всей их жизни, нет обмена или объединения экземпляров (его можно удалить из памяти после пассивации для экономии ресурсов, но это другая история) и поддерживать состояние разговора. Это означает, что переменные экземпляра компонента могут хранить данные относительно клиента между вызовами методов. И это позволяет иметь взаимозависимые вызовы методов (изменения, сделанные одним методом, влияют на последующие вызовы методов). Многоэтапные процессы (процесс регистрации, корзина покупок, процесс бронирования...) являются типичными сценариями использования SFSB.

Еще кое-что. Если вы используете SFSB, вы должны избегать внедрения их в классы, которые являются многопоточными по своей природе, такие как Servlets и управляемые bean-компоненты 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
}

В этом контексте не имеющее состояния и не имеющее состояния совсем не означает, что вы можете ожидать.

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

  • Резервное место
  • Платная кредитная карта
  • Билет на выпуск

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

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

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

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

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

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

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

Бобы без гражданства

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

Stateful Beans

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

У него хорошие ответы. Я хотел бы добавить небольшой ответ. Bean без сохранения состояния не должен использоваться для хранения данных клиента. Его следует использовать для "моделирования действий или процессов, которые можно выполнить за один раз".

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

Другие вопросы по тегам