Jakarta ee JSF请求范围bean会在每个请求上不断重新创建新的有状态会话bean吗?
我正在使用JSF、PrimeFaces、Glassfish和Netbeans构建我的第一个JavaEE应用程序。因为我是新来的,有可能我在处理核心问题时出错了 核心问题:我想安全地维护用户的信息。关于是否应该在JSF会话bean或有状态会话EJB中维护它,似乎存在着相互矛盾的观点。我尝试使用有状态会话EJB,因为这样更安全 问题是,我的应用程序似乎正在创建该bean的多个实例,而我希望它创建一个实例并重新使用它。如果我刷新页面,它将运行Jakarta ee JSF请求范围bean会在每个请求上不断重新创建新的有状态会话bean吗?,jakarta-ee,jsf-2,ejb-3.0,Jakarta Ee,Jsf 2,Ejb 3.0,我正在使用JSF、PrimeFaces、Glassfish和Netbeans构建我的第一个JavaEE应用程序。因为我是新来的,有可能我在处理核心问题时出错了 核心问题:我想安全地维护用户的信息。关于是否应该在JSF会话bean或有状态会话EJB中维护它,似乎存在着相互矛盾的观点。我尝试使用有状态会话EJB,因为这样更安全 问题是,我的应用程序似乎正在创建该bean的多个实例,而我希望它创建一个实例并重新使用它。如果我刷新页面,它将运行@PostConstruct和@postactive三次,所
@PostConstruct
和@postactive
三次,所有这些都使用不同的实例。然后,当我重新部署应用程序时,它们都会被销毁
我是否误解了它应该如何工作,或者是配置错误
我将尝试显示一个精简的代码示例:
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>
UserBean
(不包括UserBeanLocal
接口)
最后,这里是刷新三次后的Glassfish输出:
INFO:#####创建用户Bean:boundary._UserBean_Serializable@2e644784
信息:认证测试自动通过。
信息:认证测试自动通过。
信息:认证测试自动通过。
信息:创建用户Bean:边界_Serializable@691ae9e7
信息:认证测试自动通过。
信息:认证测试自动通过。
信息:认证测试自动通过。
信息:创建用户Bean:边界_Serializable@391115ac
信息:认证测试自动通过。
信息:认证测试自动通过。
信息:认证测试自动通过。
有状态会话bean(SFSB)并不是您所想象的那样。您似乎认为它们的行为在某种程度上类似于会话范围内的JSF托管bean。这是不真实的。EJB中的术语“会话”的含义与您所想到的HTTP会话完全不同
EJB中的“会话”必须在事务上下文中解释。事务(基本上是DB会话)在SFSB情况下只要客户端存在就存在。在您的特定示例中,SFSB的客户机不是webbrowser,而是JSF管理的bean实例本身,正是注入SFSB的实例。由于您已将JSF托管bean放在请求范围内,因此将在每个HTTP请求上与JSF托管bean一起重新创建SFSB
例如,尝试将JSF托管bean放在视图范围中。例如,视图范围对于同一页面上的多步骤表单非常有用。每次视图回发到自身时,相同的JSF托管bean实例将被重用,并且该实例允许您访问与创建bean时相同的SFSB的实例,该实例不是在别处共享的。SFSB事务的存在时间与客户端(视图范围的JSF托管bean)的存在时间相同
无状态会话bean(SLSB)可以在其他地方共享,但这并不重要,因为它打算被视为无状态会话bean。此“功能”节省了容器创建和存储它们的时间和内存。容器只能有一个池。更重要的是,注入视图、会话或应用程序范围的JSF托管bean中的SLSB实例不一定需要在每个HTTP请求上引用与JSF托管bean创建期间完全相同的实例。它甚至可以是完全不同的实例,具体取决于容器池中的可用实例。事务的生存期(默认情况下)与SLSB上的单个方法调用一样长
也就是说,SFSB不适用于“记住登录用户”的特定情况。它“更安全”真的毫无意义。只需将JSF托管bean放在会话范围内,让它自己记住登录的用户,并使用SLSB执行任何业务操作(例如与DB交互),并且仅当您需要真正的有状态会话bean时才使用SFSB(我假设您现在了解它们的确切含义:)
另见:
@SessionScoped
注释它。我只是不建议这种紧密耦合。虽然这很古老(而且回答得很好),但我认为值得一提的是,“当[您]重新部署应用程序时,它们都会被破坏”的原因是,当您使用@EJB注入有状态会话bean时,由应用程序触发@PreDestroy方法,否则它就挂在那里。这就是为什么您的输出从未命中cleanup()
。另见
@Named(value = "loginController")
@RequestScoped
public class LoginController implements Serializable {
@EJB
private UserBeanLocal userBean;
public boolean isAuthenticated() {
return userBean.isAuthenticated();
}
}
@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");
}
}