从请求范围的bean访问会话范围的bean

时间:2012-05-18 19:57:44

标签: jsf jsf-2 session-scope

我正在尝试使用在IceFaces页面上找到的模式。(我没有使用IceFaces,使用PrimeFaces)

在这种情况下,我有两个豆子:

UserController Usermodel

在我的 UserModel 上我有一个 UserVO 的实例(由另一个程序员创建)。 在我的 UserController 上我有这个:

@ManagedBean
@RequestScoped
public class UserController implements Serializable
{

    private static final long serialVersionUID = 1L;
    private UserBO bo;
    private UserModel model;

    public UserController()
    {
        bo = new UserBO();
        model = new UserModel();
    }

    public void Login() throws IOException
    {
        model.setUserVo(bo.executeLogin(model.getUserVo()));
        ExternalContext externalContent = FacesContext.getCurrentInstance().getExternalContext();
        if (!model.getUserVo().isError())
        {
            model.setLoggedIn(true);
            externalContent.getSessionMap().put("userSession", model);
            externalContent.redirect(externalContent.getRequestContextPath() + "/views/request/search.html");
        } else
        {
            model.setLoggedIn(false);
            FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_ERROR, model.getUserVo().getMessage(), model.getUserVo().getLogin());
            FacesContext.getCurrentInstance().addMessage(null, facesMessage);
        }
    }

    public UserBO getBo()
    {
        return bo;
    }

    public void setBo(UserBO bo)
    {
        this.bo = bo;
    }

    public UserModel getModel()
    {
        return model;
    }

    public void setModel(UserModel model)
    {
        this.model = model;
    }
}

正如您所看到的,我创建了 UserModel 的新实例,并使用bo.executeLogin()返回的内容进行设置,并且它正在运行,我的对象将被返回。

为了确保用户已登录,我的 UserModel 上有一个属性:

@ManagedBean
@SessionScoped
public class UserModel
{

    private UserVO userVo;
    private Boolean loggedIn = false;

    public UserModel()
    {
        userVo = new UserVO();
    }

    public UserVO getUserVo()
    {
        return userVo;
    }

    public void setUserVo(UserVO userVo)
    {
        this.userVo = userVo;
    }

    public Boolean getLoggedIn()
    {
        return loggedIn;
    }

    public void setLoggedIn(Boolean loggedIn)
    {
        this.loggedIn = loggedIn;
    }

我有一个 template.xhtml

<ui:fragment rendered="#{userModel.loggedIn}">
            <ui:include src="../includes/top.xhtml"/>
</ui:fragment>

事情是它不起作用,没有获得loggedIn属性值。

我的猜测是访问这种方式我有点创建 UserModel 的新实例,如果是这样,这是一个问题,因为我的 UserController 不是会话作用域,只有 UserModel

修改

我知道我可以简单地检查是否设置了 UserModel loggedIn属性而不是使用此userVo属性,但问题是关于会话范围的bean,我可以' t从 UserController 访问它,因为它不是作用域会话,所以它被设置,每个页面都将使用我的 template.xhtml

3 个答案:

答案 0 :(得分:7)

(这最初发布在https://stackoverflow.com/questions/10691324/working-with-3-java-beans-controller-backing-model,但是OP删除了这个问题,我不想丢掉答案,我认为这个问题的转发也适合)

您可能过分关注包含ICEfaces blogmainly nonsense

你应该拥有一个充当控制器的JSF托管bean,你已经拥有它:UserController。你应该有一个代表模型的简单实体bean,你已经拥有它:UserVO。您应该有一个代表该服务的企业bean,您已拥有它:UserBO。最后两个不需要是JSF托管bean。

根据您的问题历史记录,您正在使用Glassfish,因此您可以使用JPA和EJB。所以模型类应该是JPA @Entity,服务类应该是@Stateless EJB。

“登录用户”的用例是特殊的。您不希望将实体作为会话范围的托管bean,或者它将由JSF隐式创建。您最好自己直接在会话范围内,以便在用户登录时检查#{not empty user}

所有这一切都应该是这样的:

@Entity
public class User {

    private String username;
    private String password;

    // ...
}
@Stateless
public class UserService {

    @PersistenceContext
    private EntityManager em;

    public User find(String username, String password) {
        return em.createQuery("SELECT u FROM User u WHERE username = :username AND password = MD5(:password)", User.class)
           .setParameter("username", username)
           .setParameter("password", password)
           .getSingleResult();
    }

}
@ManagedBean
@ViewScoped
public class UserController {

    private String username;
    private String password;

    @EJB
    private UserService service;

    public String login() {
        FacesContext context = FacesContext.getCurrentInstance();

        try {
            User user = userService.find(username, password);
            context.getExternalContext().getSessionMap().put("user", user);
            return "/views/commons/home.html?faces-redirect=true";
        }
        catch (NoResultException) {
            context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unknown login, please try again", null));
            return null;
        }
    }

    public String logout() {
        FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
        return "/views/commons/login.html?faces-redirect=true";
    }

    // ...
}

<h:form>
    <h:inputText value="#{userController.username}" required="true" />
    <h:inputSecret value="#{userController.password}" required="true" />
    <h:commandButton value="login" action="#{userController.login}"/>
    <h:messages />
</h:form>

或者,您可以使UserController一个包含User实体的会话范围bean,您只需要添加一个额外的方法来检查用户是否已登录,以便你可以通过#{userController.loggedIn}在EL中使用它。

@ManagedBean
@SessionScoped
public class UserController {

    private User user = new User();

    @EJB
    private UserService service;

    public String login() {
        FacesContext context = FacesContext.getCurrentInstance();

        try {
            user = userService.find(user.getUsername(), user.getPassword());
            return "/views/commons/home.html?faces-redirect=true";
        }
        catch (NoResultException) {
            context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unknown login, please try again", null));
            return null;
        }
    }

    public String logout() {
        FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
        return "/views/commons/login.html?faces-redirect=true";
    }

    public boolean isLoggedIn() {
        return user.getId() != null;
    }

    // ...
}

<h:form>
    <h:inputText value="#{userController.user.username}" required="true" />
    <h:inputSecret value="#{userController.user.password}" required="true" />
    <h:commandButton value="login" action="#{userController.login}"/>
    <h:messages />
</h:form>

答案 1 :(得分:5)

不要在UserModel中创建新的UserController个实例,而是将其@ManagedProperty注入。

UserController

@ManagedProperty(value="#{userModel}")
private UserModel model;

// add getter and setter for userModel (important!)

然后你不必在构造函数中实例化它,并且总是在你的控制器中获得UserModel的会话作用域实例。

<强>更新

我认为您使用严格的MVC方法使登录过程变得复杂。在JSF中,模型,视图和控制器之间的边界有些模糊或重叠。

我建议您阅读this interesting question and answers,尤其是this answer,了解有关该主题的更多信息。

关于你的具体问题。我不太确定是什么原因,但你应该避免的是你自己实例化托管bean并且使用注入和自我初始化的bean实例。

另外,我建议将您的bean合并到一个bean中。那么你就没有循环依赖和空引用的问题。

答案 2 :(得分:0)

您可以使用依赖注入来从该bean获取具有会话范围的值。

@Inject UserModel user; 

然后您可以在UserControl bean中使用此Object。 顺便说一下,当你处理RequestScope bean时,你不需要实现serializable。