文中代码基于 Java 17
Spring 拦截器与 Servlet Filter 的异同
Spring 的拦截器(Interceptor)与 Servlet 的 Filter 有相似之处,比如二者都是 AOP 编程思想的体现,都能实现权限检查、日志记录等。
不同的是:
- 使用范围不同:Filter 是 Servlet 规范规定的,只能用于 Web 程序中。而拦截器既可以用于 Web 程序,也可以用于 Application、Swing 等程序中。
- 规范不同:Filter 是 Servlet 规范中定义的,是 Servlet 容器支持的。而拦截器是在 Spring 容器内的,是 Spring 框架支持的。
- 使用资源不同:同其他代码块一样,Interceptor 也是一个 Spring 的组件,归 Spring 管理,配置在 Spring 文件中,因此能使用 Spring 里的任何资源、对象,例如 Service 对象、数据源、事务管理等,通过 IoC 注入拦截器即可。而 Filter 则不能。
- 深度不同:Filter 只在 Servlet 前后起作用。而拦截器能够深入到方法前后、异常抛出前后等。因此拦截器的使用具有更大的弹性。所以在 Spring 架构的程序中,要优先使用拦截器。
对于一个请求,拦截器、过滤器执行流程如下:
flowchart LR A((request)) B(Filter) C(Servlet) D(Interceptor) E(Controller) F((doService)) A --> B --> C --> D --> E --> F
何时使用 Filter、Interceptor?
- 如果是非 Spring 项目,那么拦截器不能用,只能使用过滤器。
- 如果是处理
Controller
前后,既可以使用拦截器也可以使用过滤器。 - 如果是处理
DispatcherServlet
前后,只能使用过滤器。
Spring Boot 使用 Filter
Spring Boot 使用 Filter 有两种方式:
- 使用 Spring Boot 提供的
FilterRegistrationBean
注册 Filter - 使用原生 Servlet 注解
@WebFilter
定义 Filter
使用 FilterRegistrationBean
注册 Filter
- 自定义 Filter 并实现
jakarta.servlet.Filter
。(低版本 JDK 请使用javax.servlet.Filter
)
|
|
- 自定义配置类配置 Filter
|
|
使用原生 Servlet 注解 @WebFilter
定义 Filter
- 自定义 Filter 并实现
jakarta.servlet.Filter
(低版本 JDK 请使用javax.servlet.Filter
),同时添加@WebFilter
注解。
|
|
- 在启动类上添加
@ServletComponentScan
注解
|
|
自定义过滤器执行顺序
使用自定义配置类配置过滤器时,可通过 setOrder()
方法设置过滤器执行顺序。如不设置,则按照 Spring Boot Bean 加载顺序。
使用 Servlet 原生注解 @WebFilter
时,只能通过限定 Filter 类型(按字母表 A - Z 的顺序)。注意:此方式使用 @Order
注解无效。
Spring Boot 使用 Interceptor
当请求来到
DispatcherServlet
时,它会根据HandlerMapping
的机制找到处理器,这样就会返回一个HandlerExecutionChain
对象。这个对象包含处理器和拦截器。这里的拦截器会对处理器进行拦截,这样通过拦截器就可以增强处理器的功能。 ——《深入浅出 Spring Boot 2.X》- 杨开振
拦截器的设计(Interceptor 接口)
所有的拦截器都需要实现 HandlerInterceptor
接口。该接口主要定义如下:
|
|
这些方法的执行流程如下:
- 执行
preHandler()
方法。该方法会返回一个布尔值。如果为false
,则结束所有流程;如果为true
,则执行下一步 - 执行处理器逻辑。它包含控制器的功能。
- 执行
postHandle()
方法。 - 执行视图解析和视图渲染。
- 执行
afterCompletion()
方法。
开发拦截器
定义简单拦截器
实现 HandlerInterceptor
接口,并实现其方法:
|
|
注册拦截器
新建配置类并实现 WebMvcConfigurer
接口,重写 addInterceptors(InterceptorRegistry registry)
方法:
|
|
多个拦截器顺序
多个拦截器拦截同一路径时,采用责任链模式的规则,对于处理器前方法采用先注册先执行,而处理后方法和完成方法则是先注册后执行的规则。
当多个拦截器中某一个处理前(
preHandle()
)方法为false
时,则后续拦截器、处理器和所有拦截器的处理器后(postHandle()
)方法都不会执行。完成方法(afterCompletion()
)则不一样,它只会执行返回true
的拦截器的完成方法,而且顺序是先注册后执行。
感谢您的耐心阅读!来选个表情,或者留个评论吧!