Компонент в области JSF-запроса продолжает воссоздавать новые сеансовые компоненты с сохранением состояния при каждом запросе?

Я создаю свое первое приложение Java EE, используя JSF, PrimeFaces, Glassfish и Netbeans. Поскольку я новичок, возможно, я неправильно подхожу к основной проблеме.

Основная проблема: я хочу защищать информацию пользователя. Похоже, существуют противоречивые идеи о том, следует ли поддерживать его в сессионном компоненте JSF или в EJB с сохранением состояния. Я пытаюсь использовать сессионный EJB с сохранением состояния, потому что он более безопасен.

Проблема заключается в том, что мое приложение, похоже, создает несколько экземпляров этого компонента, когда я ожидаю, что он создаст один и повторно его использует. Если я обновлю страницу, она запускает @PostConstruct а также @PostActivate 3 раза, все они с разными экземплярами. Затем они все разрушаются, когда я повторно развертываю приложение.

Я неправильно понял, как это должно работать или что-то неправильно настроено?

Я постараюсь показать урезанный пример кода:

basic.xhtml:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:c="http://java.sun.com/jsp/jstl/core">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        Hello from Facelets
        <c:if test="#{loginController.authenticated}">
            Authenticated
        </c:if>
        <c:if test="#{loginController.authenticated}">
            Authenticated
        </c:if>
        <c:if test="#{loginController.authenticated}">
            Authenticated
        </c:if>
    </h:body>
</html>

LoginController:

@Named(value = "loginController")
@RequestScoped
public class LoginController implements Serializable {

    @EJB
    private UserBeanLocal userBean;

    public boolean isAuthenticated() {
        return userBean.isAuthenticated();
    }

}

UserBean (исключая UserBeanLocal интерфейс)

@Stateful
public class UserBean implements UserBeanLocal, Serializable {

    boolean authenticated = false;

    @PostConstruct
    @PostActivate
    public void setup(){
        System.out.println("##### Create user Bean: "+this.toString());
    }

    @Override
    public boolean isAuthenticated() {
        System.out.println("########## Authentication test is automatically passing.");
        authenticated = true;//hard coded for simplicity.
        return authenticated;
    }     

    @PrePassivate
    @PreDestroy
    public void cleanup(){
        System.out.println("##### Destroy user Bean");
    }

}

Наконец, вот вывод Glassfish после трехкратного обновления:

INFO: ##### Create user Bean: boundary._UserBean_Serializable@2e644784
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ##### Create user Bean: boundary._UserBean_Serializable@691ae9e7
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ##### Create user Bean: boundary._UserBean_Serializable@391115ac
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.

2 ответа

Решение

Сессионные компоненты с состоянием (SFSB) не совсем то, что вы думаете. Вы, кажется, думаете, что они ведут себя как-то как управляемые bean-компоненты JSF в сессионной области. Это неправда. Термин "сессия" в EJB имеет совершенно другое значение, чем сессия HTTP, которую вы имели в виду.

"Сеанс" в EJB должен интерпретироваться в контексте транзакций. Транзакция (в основном, сеанс БД) живет в случае SFSB до тех пор, пока живет клиент. Клиент SFSB в вашем конкретном примере - не веб-браузер, а сам экземпляр управляемого компонента JSF, именно тот, в который был введен SFSB. Поскольку вы поместили управляемый компонент JSF в область запроса, SFSB будет воссоздан при каждом HTTP-запросе вместе с управляемым компонентом JSF.

В качестве примера попробуйте поместить управляемый компонент JSF в область просмотра. Например, область просмотра полезна для многошаговой формы на той же странице. Каждый раз, когда представление отправляет назад самому себе, один и тот же экземпляр управляемого JSF-компонента будет использоваться повторно, и этот экземпляр дает вам доступ к тому же экземпляру SFSB, что и при создании компонента, который не используется в другом месте. Транзакция SFSB живет до тех пор, пока живет клиент (управляемый компонент JSF в области представления).

Сессионный компонент без сохранения состояния (SLSB) может использоваться в другом месте, но это не должно иметь значения, так как в любом случае предполагается, что он будет рассматриваться как объект без состояния. Эта "функция" экономит время и память контейнера для их создания и хранения. Контейнер может просто иметь их пул. Более того, экземпляр SLSB, который был внедрен в управляемый компонент JSF вида, сеанса или приложения, не обязательно должен ссылаться на один и тот же экземпляр в каждом HTTP-запросе, как это было при создании управляемого компонента JSF. Это может быть даже совершенно другой экземпляр, в зависимости от доступных экземпляров в пуле контейнера. Транзакция живет (по умолчанию) столько, сколько один вызов метода на SLSB.

Тем не менее, SFSB не подходит для вашего конкретного случая "запоминания вошедшего в систему пользователя". То, что это "более безопасно", не имеет никакого смысла. Просто поместите управляемый bean-компонент JSF в область действия сеанса и дайте ему самостоятельно запомнить зарегистрированного пользователя и используйте SLSB для выполнения каких-либо бизнес-действий (например, взаимодействия с БД) и используйте SFSB только тогда, когда вы хотите иметь реальное состояние с состоянием сессионный компонент (я предполагаю, что вы теперь понимаете, что именно они есть:)).

Смотрите также:

Насколько я понимаю из моего исследования и использования, EJB SFSB бесполезен для веб-приложений, поскольку JSF, Spring предоставляет полезные аннотации для сохранения сеанса для пользователя. Но в случае, когда требуется приложение вызова вызова веб-службы и вызова RPC-метода, EJB SFSB необходимо поддерживать сеанс (переход) для каждого пользователя.

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