文中代码基于 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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import java.io.IOException;
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// your code
}
}
|
- 自定义配置类配置 Filter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<MyFilter> myFilterFilterRegistration() {
FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
MyFilter myFilter = new MyFilter();
registrationBean.setFilter(myFilter);
return registrationBean;
}
}
|
使用原生 Servlet 注解 @WebFilter 定义 Filter
- 自定义 Filter 并实现
jakarta.servlet.Filter (低版本 JDK 请使用 javax.servlet.Filter),同时添加 @WebFilter 注解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// your code
}
}
|
- 在启动类上添加
@ServletComponentScan 注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
|
自定义过滤器执行顺序
使用自定义配置类配置过滤器时,可通过 setOrder() 方法设置过滤器执行顺序。如不设置,则按照 Spring Boot Bean 加载顺序。
使用 Servlet 原生注解 @WebFilter 时,只能通过限定 Filter 类型(按字母表 A - Z 的顺序)。注意:此方式使用 @Order 注解无效。
Spring Boot 使用 Interceptor
当请求来到 DispatcherServlet 时,它会根据 HandlerMapping 的机制找到处理器,这样就会返回一个 HandlerExecutionChain 对象。这个对象包含处理器和拦截器。这里的拦截器会对处理器进行拦截,这样通过拦截器就可以增强处理器的功能。 ——《深入浅出 Spring Boot 2.X》- 杨开振
拦截器的设计(Interceptor 接口)
所有的拦截器都需要实现 HandlerInterceptor 接口。该接口主要定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
|
这些方法的执行流程如下:
- 执行
preHandler() 方法。该方法会返回一个布尔值。如果为 false ,则结束所有流程;如果为 true ,则执行下一步
- 执行处理器逻辑。它包含控制器的功能。
- 执行
postHandle() 方法。
- 执行视图解析和视图渲染。
- 执行
afterCompletion() 方法。
开发拦截器
定义简单拦截器
实现 HandlerInterceptor 接口,并实现其方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("处理器执行前方法");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("处理器执行后方法");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("处理器完成方法");
}
}
|
注册拦截器
新建配置类并实现 WebMvcConfigurer 接口,重写 addInterceptors(InterceptorRegistry registry) 方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/test/*");
}
}
|
多个拦截器顺序
多个拦截器拦截同一路径时,采用责任链模式的规则,对于处理器前方法采用先注册先执行,而处理后方法和完成方法则是先注册后执行的规则。
当多个拦截器中某一个处理前(preHandle())方法为 false 时,则后续拦截器、处理器和所有拦截器的处理器后( postHandle() )方法都不会执行。完成方法( afterCompletion() )则不一样,它只会执行返回 true 的拦截器的完成方法,而且顺序是先注册后执行。