JSF请求作用域bean继续在每个请求上重新创建新的有状态会话bean吗?

Ala*_*Dee 32 java-ee ejb-3.0 jsf-2

我正在使用JSF,PrimeFaces,Glassfish和Netbeans构建我的第一个Java EE应用程序.因为我是新手,所以我可能会接近核心问题.

核心问题:我想安全地维护用户的信息.关于是否应该在JSF会话bean或有状态会话EJB中维护它似乎存在矛盾的想法.我正在尝试使用有状态会话EJB,因为它更安全.

问题是我的应用程序似乎正在创建该bean的多个实例,当我希望它创建一个并重新使用它时.如果我刷新页面它运行@PostConstruct@PostActivate3次,他们都用不同的实例.然后,当我重新部署应用程序时,它们都会被破坏.

我误解了它应该如何工作或错误配置了什么?

我将尝试显示一个修剪过的代码示例:

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>
Run Code Online (Sandbox Code Playgroud)

LoginController:

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

    @EJB
    private UserBeanLocal userBean;

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

}
Run Code Online (Sandbox Code Playgroud)

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");
    }

}
Run Code Online (Sandbox Code Playgroud)

最后,这是刷新三次后的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.
Run Code Online (Sandbox Code Playgroud)

Bal*_*usC 59

有状态会话bean(SFSB)并不完全符合您的想法.您似乎认为它们的行为类似于会话作用域的JSF托管bean.这是不真实的.EJB中的术语"会话"与您考虑过的HTTP会话具有完全不同的含义.

必须在事务上下文中解释EJB中的"会话".只要客户端存在,事务(基本上是数据库会话)就会出现在SFSB的情况下.SFSB的客户端在您的特定示例中不是 webbrowser,而是JSF托管bean实例本身,恰好是注入SFSB的实例.由于您已将JSF托管bean放在请求范围内,因此将在每个HTTP请求上与JSF托管bean一起重新创建SFSB.

例如,尝试将JSF托管bean放在视图范围中.例如,视图范围对于同一页面上的多步骤表单很有用.每次当视图回发到自身时,将重用相同的JSF托管bean实例,并且此实例允许您访问与创建bean时相同的SFSB实例,该实例不在其他地方共享.只要客户端(视图作用域的JSF托管bean)存在,SFSB事务就会存在.

无状态会话bean(SLSB)可以在其他地方共享,但这无关紧要,因为无论如何它都被视为无状态.这个"功能"节省了容器时间和内存来创建和存储它们.容器可以只有一个池.更重要的是,在视图,会话或应用程序范围内的JSF托管bean中注入的SLSB实例不一定需要在每个HTTP请求上引用与在JSF托管bean创建期间完全相同的实例.它甚至可以是完全不同的实例,具体取决于容器池中的可用实例.只要在SLSB上调用单个方法,事务就会生效(默认情况下).

也就是说,SFSB不适合您"记住登录用户"的特殊情况.它"更安全"真的没有意义.只需将JSF托管bean放在会话范围内,让它自己记住登录用户,并使用SLSB执行任何业务操作(例如与DB交互),并仅在需要真实状态时使用SFSB 会话bean(我假设你现在明白它们到底是什么:)).

也可以看看:

  • 第二篇是一篇优秀的文章.我现在看到你来自"更安全"的地方.我认为你现在很清楚它应该在事务上下文中解释,而不是在webapp上下文中解释.第一个是好点,但整体解释和例子都很薄弱.我建议[本文](http://balusc.blogspot.com/2011/09/communication-in-jsf-20.html)作为起点. (2认同)