我正在使用JSF,PrimeFaces,Glassfish和Netbeans构建我的第一个Java EE应用程序。因为我是新手,所以我可能会接近核心问题。
核心问题:我想安全地维护用户的信息。关于是否应该在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: 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.
答案 0 :(得分:58)
有状态会话bean(SFSB)并不完全符合您的想法。您似乎认为它们的行为类似于会话作用域的JSF托管bean。这是不真实的。 EJB中的术语“会话”与您考虑过的HTTP会话具有完全不同的含义。
EJB中的“会话”必须在事务上下文中解释。只要客户端存在,事务(基本上是数据库会话)就会出现在SFSB的情况下。 SFSB的客户端在您的特定示例中不 webbrowser,但是JSF托管bean实例本身,正好是注入SFSB的实例。由于您已将JSF托管bean放在请求范围内,因此将在每个HTTP请求上与JSF托管bean一起重新创建SFSB。
举个例子,尝试将JSF托管bean放在视图范围内。例如,视图范围对于同一页面上的多步骤表单很有用。每次当视图回发到自身时,将重用相同的JSF托管bean实例,并且此实例允许您访问SFSB的相同实例,就像创建bean时一样,这是<强>不在别处分享。只要客户端(视图作用域的JSF托管bean)存在,SFSB事务就会存在。
无状态会话bean(SLSB)可以在别处共享,但这无关紧要,因为它无论如何都被视为无状态。这个“功能”节省了容器时间和内存来创建和存储它们。容器可以只有一个池。更重要的是,在视图,会话或应用程序范围内的JSF托管bean中注入的SLSB实例不一定需要在每个HTTP请求上引用与在JSF托管bean创建期间完全相同的实例。它甚至可以是完全不同的实例,具体取决于容器池中的可用实例。只要在SLSB上调用单个方法,事务就会生效(默认情况下)。
也就是说,SFSB不适合您“记住登录用户”的特定情况。它“更安全”真的没有意义。只需将JSF托管bean放在会话范围内,让它自己记住登录用户,并使用SLSB执行任何业务操作(例如与数据库交互),并仅在需要真正的有状态会话bean(我假设您现在了解它们究竟是什么:) :)。
答案 1 :(得分:2)
据我的调查和使用情况而言,EJB SFSB对Web应用程序没有用,因为JSF,Spring提供了helfull注释来保持每个用户的会话。但是在webservice和RPC方法调用所需的应用程序正在运行的情况下,EJB SFSB是保持每个用户的会话(转换)的必要条件。