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

简介: 别小看Spring过滤器,这些知识点你必须得掌握!

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

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

启动程序:

image.png

Controller:

1.png

实现俩过滤器:

  • AuthFilter

1.png

  • TimeCostFilter

image.png

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


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

image.png

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

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

源码解析

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


image.png

image.png

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

1.png

发现对 StandardContext#FilterMaps 的写入是在

addFilterMapBefore()

image.png

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


addFilterMapBefore()中加入断点:

image.png

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

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


image.png

image.png

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

selfInitialize()

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

getServletContextInitializerBeans()

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

image.png

ServletContextInitializerBeans是个集合类

image.png

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

image.png

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

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

继续查看ServletContextInitializerBeans构造器:

image.png

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

image.png

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

image.png

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

目录
相关文章
|
2月前
|
负载均衡 监控 Java
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
本文详细介绍了 Spring Cloud Gateway 的核心功能与实践配置。首先讲解了网关模块的创建流程,包括依赖引入(gateway、nacos 服务发现、负载均衡)、端口与服务发现配置,以及路由规则的设置(需注意路径前缀重复与优先级 order)。接着深入解析路由断言,涵盖 After、Before、Path 等 12 种内置断言的参数、作用及配置示例,并说明了自定义断言的实现方法。随后重点阐述过滤器机制,区分路由过滤器(如 AddRequestHeader、RewritePath、RequestRateLimiter 等)与全局过滤器的作用范围与配置方式,提
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
|
1月前
|
缓存 安全 Java
《深入理解Spring》过滤器(Filter)——Web请求的第一道防线
Servlet过滤器是Java Web核心组件,可在请求进入容器时进行预处理与响应后处理,适用于日志、认证、安全、跨域等全局性功能,具有比Spring拦截器更早的执行时机和更广的覆盖范围。
|
SQL Java 测试技术
在Spring boot中 使用JWT和过滤器实现登录认证
在Spring boot中 使用JWT和过滤器实现登录认证
800 0
|
4月前
|
SQL Java 数据库连接
Spring、SpringMVC 与 MyBatis 核心知识点解析
我梳理的这些内容,涵盖了 Spring、SpringMVC 和 MyBatis 的核心知识点。 在 Spring 中,我了解到 IOC 是控制反转,把对象控制权交容器;DI 是依赖注入,有三种实现方式。Bean 有五种作用域,单例 bean 的线程安全问题及自动装配方式也清晰了。事务基于数据库和 AOP,有失效场景和七种传播行为。AOP 是面向切面编程,动态代理有 JDK 和 CGLIB 两种。 SpringMVC 的 11 步执行流程我烂熟于心,还有那些常用注解的用法。 MyBatis 里,#{} 和 ${} 的区别很关键,获取主键、处理字段与属性名不匹配的方法也掌握了。多表查询、动态
157 0
|
4月前
|
人工智能 安全 Java
Spring Boot 过滤器 拦截器 监听器
本文介绍了Spring Boot中的过滤器、拦截器和监听器的实现与应用。通过Filter接口和FilterRegistrationBean类,开发者可实现对请求和响应的数据过滤;使用HandlerInterceptor接口,可在控制器方法执行前后进行处理;利用各种监听器接口(如ServletRequestListener、HttpSessionListener等),可监听Web应用中的事件并作出响应。文章还提供了多个代码示例,帮助读者理解如何创建和配置这些组件,适用于构建更高效、安全和可控的Spring Boot应用程序。
617 0
|
11月前
|
JavaScript Java Kotlin
深入 Spring Cloud Gateway 过滤器
Spring Cloud Gateway 是新一代微服务网关框架,支持多种过滤器实现。本文详解了 `GlobalFilter`、`GatewayFilter` 和 `AbstractGatewayFilterFactory` 三种过滤器的实现方式及其应用场景,帮助开发者高效利用这些工具进行网关开发。
1600 1
|
Java API Spring
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中过滤器的基础知识和实战项目应用的教程。
394 0
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
|
Java 开发者 Spring
Spring Cloud Gateway 中,过滤器的分类有哪些?
Spring Cloud Gateway 中,过滤器的分类有哪些?
438 3
|
前端开发 Java 数据库连接