Компонент в области 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 необходимо поддерживать сеанс (переход) для каждого пользователя.