启用redis session
spring通过EnableRedisHttpSession注解来启用redid session
@Import(RedisHttpSessionConfiguration.class)
@Configuration
public @interface EnableRedisHttpSession {
int maxInactiveIntervalInSeconds() default 1800;
}
该注解有两个元注解,一个是Configuration, 一个是Import, 上一篇提过Configuration,它就相当于是beans配置,而Import则相当于是beans里嵌套了另一个beans配置项。另外再介绍一下Bean这个注解,这个注解相当于是beans配置里的bean标签,它是注解在方法上的,被注解方法的返回值就是一个spring bean,而相应的方法名就是作为bean name,如果显示的设置了name属性,那就以name属性值作为bean name。
所以可以理解为EnableRedisHttpSession就是一个编程式的配置定义,而RedisHttpSessionConfiguration显然也应该是一个编程式配置定义。
容器对springSessionRepositoryFilter的依赖管理
在RedisHttpSessionConfiguration里很惊喜的发现了第一篇里被注入的“springSessionRepositoryFilter”。
@Bean
public <S extends ExpiringSession> SessionRepositoryFilter<? extends ExpiringSession> springSessionRepositoryFilter(SessionRepository<S> sessionRepository, ServletContext servletContext) {
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<S>(sessionRepository);
sessionRepositoryFilter.setServletContext(servletContext);
if(httpSessionStrategy != null) {
sessionRepositoryFilter.setHttpSessionStrategy(httpSessionStrategy);
}
return sessionRepositoryFilter;
}
这里相当于是定义了一个bean-name=”springSessionRepositoryFilter” class=”org.springframework.session.web.http.SessionRepositoryFilter”的bean。
两个参数SessionRepository和ServletContext都是由Spring容器管理的依赖,SessionRepository和SpringSessionRepositoryFilter定义在同一个Configuration里。ServletContext的注入可能会比较令人疑惑,在这个Configuration没有定义它无法定义它,因为它是由Servlet容器生成的。
ContextLoaderListener
在介绍ServletContext的依赖之前,再聊下前文提过的ContextLoaderListener。它由initializer动态注册,其构造函数参数为一个WebApplicationContext,继承于ContextLoader(实际启动WebApplicationContext初始化工作的对象),构造函数参数是一个WebApplicationContext。
Servlet容器启动时会通知ContextLoaderListener,
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
ContextLoaderListener会调用父类ContextLoader的initWebApplicationContext方法来启动ApplicationContext的初始化,
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
configureAndRefreshWebApplicationContext(cwac, servletContext);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
return this.context;
}
上面只抽取了ContextLoader的关键逻辑
1. ApplicationContext被默认放入了ServletContext中,key是个默认值。上一篇提过在获取对应name的filter时会从ServletContext里先得ApplicationContext,再获取对应依赖。
2. configureAndRefreshWebApplicationContext这个方法最终启动了ApplictionContext的最核心refresh,这个方法被放在公共抽象类AbstractApplicationContext里,几乎所有的ApplicationContext都会执行。这个阶段结束后,ApplicationContext也就启动完毕,整个容器的依赖也就完成。因为篇幅太长ApplicationContext启动的细节就不再展开。
ServletContext的依赖
在容器refresh阶段会通过postProcessBeanFactory这个策略方法对容器对应的BeanFactory做后处理,一般是设置一些beanPostProcessor之类,对于WebApplicationContext则会处理ServletContext的依赖。
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
//在这里将ServletContext 作为一个bean管理起来WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}
至此,springSessionRepositoryFilter的依赖也注入完毕。综合1,2两篇基本上就了解了filter是如何被spring容器管理并如何通过代理动态注册给容器的。那么这个拦截器究竟能做什么?这就留到后面专门通过一篇文章来展开