从会话侦听器访问和修改Application-Scoped Managed Bean的属性

时间:2012-05-15 14:52:11

标签: java jsf java-ee jsf-2

我需要访问一个应用程序范围的托管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是否可以通过上述方式访问。

有什么想法吗?

1 个答案:

答案 0 :(得分:3)

FacesContext仅在服务于已调用FacesServlet的webbrowser发起的HTTP请求的线程中可用。在会话销毁期间,不一定是HTTP请求的手段。会话通常由容器管理的后台线程销毁。这不会通过FacesServlet调用HTTP请求。因此,您不应期望FacesContext在会话销毁期间始终存在。只有当您在JSF托管bean中调用session.invalidate()时,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);
    }

    // ...
}

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

@ManagedBean
@SessionScoped
public class User {

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

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

    // ...
}

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