0

我需要访问应用程序范围的托管 bean 以从 HttpSessionListener 中修改某些属性。

我已经使用了以下内容:

@Override
public void sessionDestroyed(HttpSessionEvent se) {
    HttpSession session = se.getSession();
    User user = userService.findBySessionId(session.getId());    

    ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();

     ApplicationScopedBean appBean = (ApplicationScopedBean) externalContext.getApplicationMap().get("appBean");

     appBean.getConnectedUsers().remove(user);
}

externalContext = FacesContext.getCurrentInstance().getExternalContext() 已经在这里导致了一个空指针异常,即使它没有,我也不确定 appBean 是否可以通过上述方式访问。

有任何想法吗?

4

1 回答 1

3

仅在FacesContext服务于由调用FacesServlet. 在会话销毁期间,不一定意味着 HTTP 请求。会话通常由容器管理的后台线程销毁。这不会通过FacesServlet. 因此,您不应期望FacesContext在会话销毁期间始终存在。仅当您session.invalidate()在 JSF 托管 bean 内部调用时,FacesContext它才可用。

如果您的应用程序范围的托管 bean 由 JSF 管理@ManagedBean,那么很高兴知道 JSF 将其作为ServletContext. 反过来ServletContext在会话监听器中可用HttpSession#getServletContext()

所以,这应该这样做:

@Override
public void sessionDestroyed(HttpSessionEvent se) {
    HttpSession session = se.getSession();
    User user = userService.findBySessionId(session.getId());    
    ApplicationScopedBean appBean = (ApplicationScopedBean) session.getServletContext().getAttribute("appBean");
    appBean.getConnectedUsers().remove(user);
}

如果您正在运行支持 Servlet 3.0 的容器,则另一种方法是让您的应用程序范围的 bean 实现HttpSessionListener并在构造时将其自身注册。这样您就可以直接引用该connectedUsers属性。

@ManagedBean
@ApplicationScoped
public class AppBean implements HttpSessionListener {

    public AppBean() {
        ServletContext context = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext();
        context.addListener(this);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        User user = userService.findBySessionId(session.getId());    
        connectedUsers.remove(user);
    }

    // ...
}

另一种选择是将会话范围内的 bean 保留User为会话范围的托管 bean。然后,您可以使用@PreDestroy注释来标记应在会话被销毁时调用的方法。

@ManagedBean
@SessionScoped
public class User {

    @ManagedProperty("#{appBean}")
    private AppBean appBean;

    @PreDestroy
    public void destroy() {
        appBean.getConnectedUsers().remove(this);
    }

    // ...
}

这具有额外的好处,即User在 EL 上下文中可用作#{user}.

于 2012-05-15T16:38:37.323 回答