上次碰到一个需求需要对用户等登录行为进行统计,所以需要对用户的Session生命周期进行监听
ssm项目中比较简单只需要自定义一个j监听器类实现HttpSessionListener接口然后实现里边的
即可,如下图
public class LoginSessionListener implements HttpSessionAttributeListener, HttpSessionListener {
public void sessionCreated(HttpSessionEvent var1){
}
public void sessionDestroyed(HttpSessionEvent var1){
}
}
然后在web.xml中像注册Spring初始化监听器一样注册这个监听器就行
但是在对用户进行操作的时候发现了问题:由于Spring启动对IOC容器初始化也是监听的Servlet的初始化之后才开始初始化,但是Servlet的初始化是由Servlet容器在启动时初始化的(一般我们使用的较多的就是Tomcat),然后在初始化完Servlet之后在对过滤器,监听器进行初始化;这样就导致一个问题——由于监听器是有Servlet容器进行初始化的,他执行在Spring IOC容器初始化之前,导致我们自己本身定义的监听器不能被Spring初始化到IOC容器,就不能使用Spring的依赖注入特性了,这个明显是不行的
但是仔细一想Ioc容器时初始化之后是保存在全局的Servlet上下文中的,在session的生命周期事件监听中都会传入一个HttpSessionEvent对象,该对象就是事件对象,里边包含了触发该事件的session对象,然后我们就可以从HttpSessionEvent中获取session对象,再获取Servlet上下文对象,然后从servlet上下文中取出Ioc容器对象也就是ApplicationContext对象;通过百度得知Ioc容器存放在Servlet上下文中的键是在WebApplicationContext这个接口类中,它里边有一些静态常量,如下
public interface WebApplicationContext extends ApplicationContext {
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
String SCOPE_REQUEST = "request";
String SCOPE_SESSION = "session";
String SCOPE_GLOBAL_SESSION = "globalSession";
String SCOPE_APPLICATION = "application";
String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
ServletContext getServletContext();
}
其中第一个常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 就是Ioc容器保存在Servlet上下文中的键,由此我们可以直接得到Ioc容器,然后再去除我们想要的Service类等,如下
ApplicationContext applicationContext = (ApplicationContext)session.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
UsersService usersService = applicationContext.getBean(UsersService.class);
除了这种之外还有一种实现方法,Spring针对这种情况提供的有一个工具类WebApplicationContextUtils,有一个getWebApplicationContext方法,传入一个ServletContext对象返回一个WebApplicationContext对象,就是前边存放静态常量的那个接口类,它实际上是Application的一个子接口,是Spring在Web环境下的默认接口类;获取这个类之后就可以直接获取我们想要的Bean了,如下
WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(session.getServletContext());
UsersService usersService = applicationContext.getBean(UsersService.class);
然后再对这个操作进行一个简单的封装
private <T> T getBeanFromServletContext(ServletContext servletContext, Class<T> requireType) {
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
return context.getBean(requireType);
}
想从Ioc容器获取什么bean就可以直接调用这个方法传入ServletContext和所需要的Bean的class类对象就可以了
本来这个方式实现是没什么问题的,但是我在实际使用中却发现了一个问题,注入的Service类中所依赖的Mapper类对象为空,这就导致功能仍然不能实现,这里先记录一下,将来有时间再深入研究这个问题