且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

别小看Spring过滤器,这些知识点你必须得掌握!(上)

更新时间:2022-06-22 02:59:27

容器启动时过滤器初始化以及排序注册相关逻辑。

1 @WebFilter过滤器使用@Order无效

启动程序:

别小看Spring过滤器,这些知识点你必须得掌握!(上)

Controller:

别小看Spring过滤器,这些知识点你必须得掌握!(上)

实现俩过滤器:

  • AuthFilter

别小看Spring过滤器,这些知识点你必须得掌握!(上)

  • TimeCostFilter

别小看Spring过滤器,这些知识点你必须得掌握!(上)

使用 @Order,期望 TimeCostFilter 先被执行,因为TimeCostFilter原本设计是统计这个接口性能,所以需要统计AuthFilter执行的授权过程。


全部代码实现完毕,执行结果如下:

别小看Spring过滤器,这些知识点你必须得掌握!(上)

观察日志,执行时间并不包含授权过程,所以这违背预期,明明加了 @Order呀!

但若交换Order指定的值,发现也没效果,why?Order不能排序WebFilter?

源码解析

当请求来时,会执行到 StandardWrapperValve#invoke(),创建 ApplicationFilterChain,并通过ApplicationFilterChain#doFilter() 触发过滤器执行,并最终执行到内部私有方法internalDoFilter(),尝试在internalDoFilter()中寻找一些启示:


别小看Spring过滤器,这些知识点你必须得掌握!(上)

别小看Spring过滤器,这些知识点你必须得掌握!(上)

可知,过滤器执行顺序由实例变量Filters维护,Filters是createFilterChain()在容器启动时顺序遍历StandardContext中的成员变量FilterMaps所获:

别小看Spring过滤器,这些知识点你必须得掌握!(上)

发现对 StandardContext#FilterMaps 的写入是在

addFilterMapBefore()

别小看Spring过滤器,这些知识点你必须得掌握!(上)

过滤器的执行顺序由StandardContext#FilterMaps顺序决定,而FilterMaps则是一个包装过的数组,所以只要进一步弄清FilterMaps中各元素的排列顺序即可。


addFilterMapBefore()中加入断点:

别小看Spring过滤器,这些知识点你必须得掌握!(上)

Spring从selfInitialize()一直调用到addFilterMapBefore()。

selfInitialize()通过调用getServletContextInitializerBeans()获取所有ServletContextInitializer类型Bean,并调用该Bean的onStartup(),最终调用到addFilterMapBefore()。


别小看Spring过滤器,这些知识点你必须得掌握!(上)

别小看Spring过滤器,这些知识点你必须得掌握!(上)

上述的selfInitialize()又从何处调用过来呢?

selfInitialize()

  • getServletContextInitializerBeans()
    返回的ServletContextInitializer类型Beans顺序=》
    决定了addFilterMapBefore()调用顺序=》
    决定FilterMaps内元素顺序=》
    最终决定了过滤器的执行顺序。

getServletContextInitializerBeans()

仅返回了ServletContextInitializerBeans类的一个实例,参考代码如下:

别小看Spring过滤器,这些知识点你必须得掌握!(上)

ServletContextInitializerBeans是个集合类

别小看Spring过滤器,这些知识点你必须得掌握!(上)

所以,selfInitialize()能遍历 ServletContextInitializerBeans,查看其iterator()所遍之物:

别小看Spring过滤器,这些知识点你必须得掌握!(上)

对外暴露的集合遍历元素为sortedList成员变量,即selfInitialize()最终遍历sortedList。

综上,sortedList中的过滤器Bean元素顺序决定最终过滤器的执行顺序。

继续查看ServletContextInitializerBeans构造器:

别小看Spring过滤器,这些知识点你必须得掌握!(上)

第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子类:

别小看Spring过滤器,这些知识点你必须得掌握!(上)

根据不同类型,调用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

别小看Spring过滤器,这些知识点你必须得掌握!(上)

我们没有定义FilterRegistrationBean,那这里FilterRegistrationBean在哪里被定义出的?其order类成员变量是否有特定取值逻辑?