容器启动时过滤器初始化以及排序注册相关逻辑。
1 @WebFilter过滤器使用@Order无效
启动程序:
Controller:
实现俩过滤器:
- AuthFilter
- TimeCostFilter
使用 @Order,期望 TimeCostFilter 先被执行,因为TimeCostFilter原本设计是统计这个接口性能,所以需要统计AuthFilter执行的授权过程。
全部代码实现完毕,执行结果如下:
观察日志,执行时间并不包含授权过程,所以这违背预期,明明加了 @Order呀!
但若交换Order指定的值,发现也没效果,why?Order不能排序WebFilter?
源码解析
当请求来时,会执行到 StandardWrapperValve#invoke(),创建 ApplicationFilterChain,并通过ApplicationFilterChain#doFilter() 触发过滤器执行,并最终执行到内部私有方法internalDoFilter(),尝试在internalDoFilter()中寻找一些启示:
可知,过滤器执行顺序由实例变量Filters维护,Filters是createFilterChain()在容器启动时顺序遍历StandardContext中的成员变量FilterMaps所获:
发现对 StandardContext#FilterMaps 的写入是在
addFilterMapBefore()
过滤器的执行顺序由StandardContext#FilterMaps顺序决定,而FilterMaps则是一个包装过的数组,所以只要进一步弄清FilterMaps中各元素的排列顺序即可。
addFilterMapBefore()中加入断点:
Spring从selfInitialize()一直调用到addFilterMapBefore()。
selfInitialize()通过调用getServletContextInitializerBeans()获取所有ServletContextInitializer类型Bean,并调用该Bean的onStartup(),最终调用到addFilterMapBefore()。
上述的selfInitialize()又从何处调用过来呢?
selfInitialize()
- getServletContextInitializerBeans()
返回的ServletContextInitializer类型Beans顺序=》
决定了addFilterMapBefore()调用顺序=》
决定FilterMaps内元素顺序=》
最终决定了过滤器的执行顺序。
getServletContextInitializerBeans()
仅返回了ServletContextInitializerBeans类的一个实例,参考代码如下:
ServletContextInitializerBeans是个集合类
所以,selfInitialize()能遍历 ServletContextInitializerBeans,查看其iterator()所遍之物:
对外暴露的集合遍历元素为sortedList成员变量,即selfInitialize()最终遍历sortedList。
综上,sortedList中的过滤器Bean元素顺序决定最终过滤器的执行顺序。
继续查看ServletContextInitializerBeans构造器:
第87行可知:sortedList
元素顺序由this.initializers.values
通过AnnotationAwareOrderComparator排序而得。
AnnotationAwareOrderComparator通过两种方式获取比较器需要的order值以决定sortedInitializers的排列顺序:
- 待排序的对象元素实现Order接口,则通过 getOrder() 获取order值
- 否则执行 OrderUtils.findOrder() 获取该对象类 @Order 的属性
因为this.initializers.values
类型为ServletContextInitializer,其实现了Ordered接口,所以此处比较器使用 getOrder() 获取order值,对应实例变量order
。
如下方法均构建了ServletContextInitializer子类的实例,并添加到了this.initializers
:
- addAdaptableBeans()
- addServletContextInitializerBeans()
这里只看addServletContextInitializerBeans,因为使用 @WebFilter 标记添加过滤器的方案最终只会通过该方法生效:实例化并注册了所有的ServletRegistrationBean、FilterRegistrationBean以及ServletListenerRegistrationBean。
在这个方法中,Spring通过 getOrderedBeansOfType() 实例化了所有ServletContextInitializer子类:
根据不同类型,调用addServletContextInitializerBean(),ServletContextInitializer子类包括:
- ServletRegistrationBean
- FilterRegistrationBean
- ServletListenerRegistrationBean
正好对应了Servlet三大要素。
现在只关心对应Filter的FilterRegistrationBean,显然,FilterRegistrationBean是ServletContextInitializer的子类(实现了Ordered接口),同样由成员变量order的值决定其执行的优先级。
private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory) { ... if (initializer instanceof FilterRegistrationBean) { Filter source = ((FilterRegistrationBean<?>) initializer).getFilter(); addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source); } ... }
最终添加到this.initializers
:
我们没有定义FilterRegistrationBean,那这里FilterRegistrationBean在哪里被定义出的?其order类成员变量是否有特定取值逻辑?