JSF-запрос в области bean поддерживает воссоздание нового сеанса с учетом состояния beans по каждому запросу?
Я создаю свое первое приложение Java EE, используя JSF, PrimeFaces, Glassfish и Netbeans. Поскольку я новичок, возможно, я неправильно подхожу к основной проблеме.
Основная проблема: я хочу поддерживать безопасную информацию пользователя. Похоже, что существуют противоречивые идеи о том, следует ли поддерживать его в сеансе JSF bean или сеансе EJB с состоянием. Я пытаюсь использовать сессионный EJB, поскольку он более безопасен таким образом.
Проблема заключается в том, что мое приложение, похоже, создает несколько экземпляров этого bean, когда я ожидаю, что он его создаст и повторно его использует. Если я обновляю страницу, она запускает @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: [email protected]
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ##### Create user Bean: [email protected]
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ##### Create user Bean: [email protected]
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
INFO: ########## Authentication test is automatically passing.
Ответы
Ответ 1
Стойкая сессия beans (SFSB) не совсем то, что вы думаете. Кажется, вы думаете, что они ведут себя как-то вроде управляемого сессионным JSF beans. Это неверно. Термин "сеанс" в EJB имеет совершенно иное значение, чем сеанс HTTP, который вы имели в виду.
"Сессия" в EJB должна интерпретироваться в транзакционном контексте. Транзакция (в основном, сеанс БД) живет в случае SFSB, пока клиент живет. Клиент SFSB находится в вашем конкретном примере не веб-браузера, но сам JSF управляет bean экземпляром, именно тем, где вводится SFSB. Поскольку вы поместили управляемый JSF bean в область запроса, SFSB будет воссоздаваться по каждому HTTP-запросу вместе с управляемым JSF bean.
В качестве примера попробуйте поместить управляемый JSF bean в область представления. Например, область просмотра полезна для многоступенчатой формы на той же странице. Каждый раз, когда представление возвращается к самому себе, то тот же JSF-управляемый экземпляр bean будет повторно использован, и этот экземпляр дает вам доступ к экземпляру того же SFSB, как это было при создании bean, который не доступен в другом месте. Транзакция SFSB сохраняется до тех пор, пока не будет проживать клиент (видимый в JSF bean).
Сегмент безстоящего состояния bean (SLSB) можно использовать в другом месте, но это не имеет значения, поскольку он намерен в любом случае рассматриваться как апатрид. Эта "функция" сохраняет время и память контейнера для их создания и хранения. В контейнере может быть только пул. Более того, экземпляр SLSB, который был инъецирован в управляемый JSF-просмотр, сеанс или приложение, с поддержкой JSF bean, необязательно должен ссылаться на один и тот же экземпляр на каждый HTTP-запрос, как это было во время создания bean, созданного JSF. Это может быть даже совсем другой экземпляр, в зависимости от доступных экземпляров в пуле контейнеров. Транзакция живет (по умолчанию) до тех пор, пока один вызов метода на SLSB.
Тем не менее, SFSB не подходит для вашего конкретного случая "запомнить зарегистрированного пользователя". То, что это "более безопасно", действительно не имеет смысла. Просто поставьте управляемый JSF bean в области сеанса и позвольте ему запомнить сам зарегистрированный пользователь и использовать SLSB для выполнения любых бизнес-действий (например, взаимодействия с БД) и использования SFSB только тогда, когда вы хотите, чтобы real stateful session bean (я предполагаю, что теперь вы понимаете, что именно они:)).
См. также:
Ответ 2
Насколько я понимаю из моего исследования и использования, EJB SFSB не полезен для веб-приложений, поскольку JSF, Spring предоставляет helfull аннотацию, чтобы сохранить сеанс на пользователя. Но в случае, когда выполняется запрос вызова вызова webservice и вызова RPC, EJB SFSB является обязательным для сохранения сеанса (трансакции) для каждого пользователя.