servletcontainerinitializer 也是 servlet 3.0 新增的一个接口,主要用于在容器启动阶段通过编程风格注册filter, servlet以及listener,以取代通过web.xml配置注册。这样就利于开发内聚的web应用框架。
例如spring,我们使用它的web功能时,需要在web.xml中依据spring的规范新建一堆配置。这样就相当于将框架和容器紧耦合了。而在3.x后注册的功能内聚到spring里,spring-web就变成一个纯粹的即插即用的组件,不用依据应用环境定义一套新的配置。
容器启动阶段依据java spi获取到所有servletcontainerinitializer的实现类,然后执行ononstartup方法
实现类需要通过java spi声明自己是servletcontainerinitializer 的provider。
可以通过@handlestypes注解定义希望处理类型,容器会将当前应用中所有这一类型(继承或者实现)的类放在集合参数1中传递进来。如果不定义处理类型,或者应用中不存在相应的实现类,则集合参数1为空。
spring-web通过java spi声明了springservletcontainerinitializer。参考spring-web.jar下的meta-inf/services
spring-session只处理webapplicationinitializer类型,全部实例化后再依次调用webapplicationinitializer的onstartup接口。
该接口定义了一个onstartup的方法,所以实现类都需要自己实现。spring-session提供了一个抽象的公用基类–org.springframework.session.web.context.abstracthttpsessionapplicationinitializer,由它实现了接口的onstartup方法
这个方法的主要过程:
添加contextloaderlistener,使用过spring-mvc的肯定对这个listener不陌生,这个listener是整个spring-web的核心,由它来自动装配spring的核心容器applicationcontext。第一步中的applicationcontext作为构造参数传进了该listener。web容器启动时会通知该listener,该listener就会启动applicationcontext的生命周期。并将bean加载完毕。因为这是spring-web边界内的,所以就不再展开,只要知道大概做了什么事情就行了。
通过insertsessionrepositoryfilter注册了一个filter,从名字知道这个filter叫sessionrepositoryfilter,顾名思义,它肯定是通过一定方式做session持久化的。
这个方法主要是动态装载了一个delegatingfilterproxy,构造函数的入参是个静态变量,对应的值是“springsessionrepositoryfilter”。从“delegatingfilterproxy”这个名字就大概能猜到这个filter最终会把请求delegate给具体的filter,看下入参大概就可以猜到应该是委派给“springsessionrepositoryfilter”这个filter,并且这个filter应该是由spring容器管理的,在filter生命周期的某个阶段会通过spring的依赖注入进来,并执行相关的拦截请求。
这里有一点会比较容易疑惑,spring为什么要通过一个代理类来做委派,直接注册springsessionrepositoryfilter不是更好。
关于这个问题会在下一章”容器对springsessionrepositoryfilter的依赖管理“里详细展开,这里继续沿着处理流往下。
这个类继承自genericfilterbean,genericfilterbean是spring抽象出来的一个filter公用接口。先看下genericfilterbean的init方法:
这里只抽出了其中的核心逻辑,最后调用子类的initfilterbean
delegatingfilterproxy覆盖了父类的initfilterbean方法
initfilterbean方法通过spring容器来取得具体的filter bean,而webapplicationcontext对象就是上文提到的annotationconfigwebapplicationcontext对象。contextloaderlistener在spring容器refresh完后会把对应的容器保存进servletcontext里,默认的key值为webapplicationcontext.class.getname() + “.root”。这里获取spring context的findwebapplicationcontext方法就是从servletcontext里通过对应的key取得的。
至此delegatingfilterproxy初始化完成,里面做实际处理的filter最终通过spring的依赖注入初始化并且注入进来。