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

我需要访问应用程序范围的托管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()在这里导致空指针exception,即使它没有,我也不确定appBean是否可以通过上述方式访问。

有任何想法吗?

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}