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

时间:2012-01-16 22:24:23

标签: java-ee jsf-2 ejb-3.0

我正在使用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.

2 个答案:

答案 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是保持每个用户的会话(转换)的必要条件。