SpringBoot 拦截器和过滤器的区别
SpringBoot 中的拦截器(Interceptor)和过滤器(Filter)都是用于请求处理的增强机制,但它们存在以下核心区别:
对比维度 | 过滤器(Filter) | 拦截器(Interceptor) |
---|---|---|
所属规范 | Servlet 规范( javax.servlet 包) | Spring 框架( org.springframework 包) |
应用范围 | 所有进入容器的请求(包括静态资源) | 仅针对 Spring MVC 控制器的请求 |
触发时机 | 在 Servlet 容器处理请求前/后 | 在 Spring MVC 处理器执行前/后/完成时 |
实现方式 | 实现 javax.servlet.Filter 接口 |
实现 org.springframework.web.servlet.HandlerInterceptor 接口 |
依赖注入 | 不支持(Filter 由 Servlet 容器管理) | 支持(Interceptor 由 Spring 容器管理) |
访问上下文 | 仅能访问 ServletRequest 和 ServletResponse | 可访问 Handler、ModelAndView 等 Spring 组件 |
执行顺序 | 基于 FilterRegistrationBean 配置的顺序 | 基于 InterceptorRegistry 注册的顺序 |
设计模式分析
过滤器(Filter)采用的设计模式
责任链模式(Chain of Responsibility)
- 多个 Filter 形成链式调用,每个 Filter 可以决定是否继续传递请求到下一个环节
- 示例代码体现:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 前置处理 chain.doFilter(request, response); // 传递到下一个过滤器或Servlet // 后置处理 }
装饰器模式(Decorator)
- 通过 Filter 包装原始请求/响应对象,增强其功能(如字符编码处理)
- 示例:
CharacterEncodingFilter
装饰请求对象以设置字符编码
拦截器(Interceptor)采用的设计模式
AOP(面向切面编程)
- 拦截器本质是 Spring AOP 的一种实现,通过对 Handler 方法的环绕通知实现增强
- 示例:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 前置处理,返回true继续执行后续拦截器和Handler return true; }
观察者模式(Observer)
- 拦截器的
postHandle
和afterCompletion
方法类似于事件监听机制 - 当请求处理完成或发生异常时,触发相应的回调方法
- 拦截器的
典型应用场景对比
场景 | 过滤器(Filter) | 拦截器(Interceptor) |
---|---|---|
请求编码处理 | CharacterEncodingFilter | - |
跨域请求处理 | CorsFilter | - |
权限校验 | 基于 Servlet API 的简单权限检查 | 基于 Spring Security 的复杂权限控制 |
请求日志记录 | 记录原始请求信息 | 记录完整的处理时间和结果 |
事务管理 | - | 基于 @Transactional 注解的事务拦截 |
性能监控 | 统计请求总耗时 | 统计 Controller 方法执行耗时 |
选择建议
- 使用过滤器:需要对所有请求进行统一处理(如编码、安全头设置)
- 使用拦截器:需要访问 Spring 容器中的 Bean 或处理 MVC 相关对象(如 ModelAndView)
- 组合使用:复杂场景下,可结合过滤器和拦截器实现多层级处理(如:过滤器处理基础安全,拦截器处理业务权限)
下面将详细介绍Spring Boot中过滤器(Filter)和拦截器(Interceptor)的使用方法,并提供示例代码:
一、过滤器(Filter)的使用步骤
1. 创建Filter类并实现Filter接口
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter(urlPatterns = "/*", filterName = "requestLogFilter")
public class RequestLogFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 前置处理:记录请求信息
System.out.println("Filter: 请求URL - " + httpRequest.getRequestURI());
// 传递请求到下一个过滤器或Servlet
chain.doFilter(request, response);
// 后置处理:记录响应信息
System.out.println("Filter: 请求处理完成");
}
}
2. 启用Servlet组件扫描(如果未使用@ServletComponentScan)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan // 启用Servlet组件扫描
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3. 或通过Java配置注册Filter(推荐方式)
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<RequestLogFilter> loggingFilter() {
FilterRegistrationBean<RequestLogFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new RequestLogFilter());
registrationBean.addUrlPatterns("/*"); // 拦截所有请求
registrationBean.setOrder(1); // 设置过滤器执行顺序
return registrationBean;
}
}
二、拦截器(Interceptor)的使用步骤
1. 创建Interceptor类并实现HandlerInterceptor接口
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class RequestTimeInterceptor implements HandlerInterceptor {
private ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 前置处理:记录请求开始时间
long startTime = System.currentTimeMillis();
startTimeThreadLocal.set(startTime);
System.out.println("Interceptor: 请求开始 - " + request.getRequestURI());
return true; // 返回true继续执行后续拦截器和Handler
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) {
// 后置处理:Controller方法执行完成后,但视图渲染前
System.out.println("Interceptor: 请求处理中 - " + request.getRequestURI());
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
// 完成处理:视图渲染完成后,可用于资源清理
long startTime = startTimeThreadLocal.get();
long endTime = System.currentTimeMillis();
System.out.println("Interceptor: 请求完成 - " + request.getRequestURI()
+ " 耗时: " + (endTime - startTime) + "ms");
startTimeThreadLocal.remove(); // 防止内存泄漏
}
}
2. 注册拦截器到Spring MVC
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 RequestTimeInterceptor())
.addPathPatterns("/**") // 拦截所有请求
.excludePathPatterns("/static/**", "/error"); // 排除静态资源和错误页面
}
}
三、过滤器 vs 拦截器执行顺序示例
执行顺序配置
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册第一个拦截器(order=1)
registry.addInterceptor(new FirstInterceptor()).addPathPatterns("/**").order(1);
// 注册第二个拦截器(order=2)
registry.addInterceptor(new SecondInterceptor()).addPathPatterns("/**").order(2);
}
}
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<FirstFilter> firstFilter() {
FilterRegistrationBean<FirstFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new FirstFilter());
registration.addUrlPatterns("/*");
registration.setOrder(1); // 过滤器顺序1
return registration;
}
@Bean
public FilterRegistrationBean<SecondFilter> secondFilter() {
FilterRegistrationBean<SecondFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new SecondFilter());
registration.addUrlPatterns("/*");
registration.setOrder(2); // 过滤器顺序2
return registration;
}
}
执行顺序结果
对于一个HTTP请求,执行顺序为:
FirstFilter.pre -> SecondFilter.pre ->
FirstInterceptor.pre -> SecondInterceptor.pre ->
Controller处理请求 ->
SecondInterceptor.post -> FirstInterceptor.post ->
视图渲染 ->
SecondInterceptor.after -> FirstInterceptor.after ->
SecondFilter.post -> FirstFilter.post
四、常见应用场景代码示例
1. 权限校验拦截器
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String token = request.getHeader("Authorization");
if (token == null || !validateToken(token)) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "未授权");
return false;
}
// 将用户信息存入Request,供后续使用
request.setAttribute("user", parseUserFromToken(token));
return true;
}
private boolean validateToken(String token) {
// 验证Token逻辑
return token.startsWith("Bearer ");
}
private User parseUserFromToken(String token) {
// 解析用户信息
return new User("testUser");
}
}
2. 字符编码过滤器
@WebFilter(urlPatterns = "/*", filterName = "encodingFilter")
public class EncodingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
}
}
五、关键配置说明
配置项 | 过滤器(Filter) | 拦截器(Interceptor) |
---|---|---|
拦截路径配置 | @WebFilter(urlPatterns = "/*") |
registry.addPathPatterns("/**") |
排除路径配置 | 不直接支持,需在doFilter中手动判断 | registry.excludePathPatterns("/static/**") |
执行顺序控制 | 通过 FilterRegistrationBean.setOrder() |
通过 addInterceptor().order() 或注册顺序 |
依赖注入 | 不支持(非Spring管理) | 支持(可注入Service等Bean) |
获取Spring上下文 | 需手动获取ApplicationContext | 可直接注入所需组件 |
六、注意事项
过滤器注意点:
- Filter由Servlet容器管理,生命周期独立于Spring
- 无法直接注入Spring Bean,需通过ApplicationContext获取
- 对所有请求生效,包括静态资源和错误请求
拦截器注意点:
- Interceptor由Spring管理,可使用依赖注入
- 仅拦截Spring MVC处理的请求,对静态资源无效
- 可访问HandlerMethod、ModelAndView等Spring MVC特有对象
性能考虑:
- 过滤器执行更早,性能开销更小
- 拦截器功能更强大,但依赖Spring MVC上下文
根据实际需求合理选择过滤器或拦截器,复杂场景可组合使用两者。