介绍
过滤器(Filter)是 Servlet 规范中的组件,运行在 Servlet 容器中,可对所有 HTTP 请求进行预处理和后处理(如编码设置、跨域配置),依赖Servlet API且执行时机最早;拦截器(Interceptor)是 Spring MVC 框架的组件,仅拦截 Controller 层请求,可获取 HandlerMethod 信息(如方法参数、注解),适合权限验证、日志记录等业务层面的拦截。
如上图所示(图片来自网络),在基于 Tomcat 和 Servlet 的 Web 应用架构中,过滤器(Filter)与拦截器(Interceptor)虽均承担请求处理的拦截与增强职责,但其作用范围与执行阶段存在显著差异。从架构层面来看,Tomcat 作为 Servlet 容器,是整个 Web 应用的运行载体,而 Spring MVC 应用本质上是基于 Servlet 规范构建的上层框架。
- 过滤器处于 Tomcat 容器与 Servlet 应用之间,在请求进入 Servlet 前或响应离开 Servlet 后执行,能够对所有流经 Tomcat 的 HTTP 请求进行拦截处理,包括静态资源请求和动态业务请求,适合处理跨域配置、字符编码转换、请求日志记录等与 Servlet 容器紧密相关的通用逻辑。
- 拦截器则深度嵌入 Spring MVC 框架内部,仅对进入 Servlet 后的 Controller 层请求生效(动态业务请求),可获取方法注解、参数等 Spring MVC 特有的上下文信息,更适用于权限校验、业务逻辑前置处理、性能监控等聚焦于 Spring 应用内部业务逻辑的场景。
过滤器 Filter
实现 Filter 接口
要编写一个自定义的 Filter,可以实现 javax.servlet.Filter 接口,并重写该接口中的三个方法。具体来说,这三个方法分别是:
- init(FilterConfig filterConfig),它在过滤器初始化时被调用,通常用于加载初始化参数或进行其他配置操作,只执行一次。如果没有特别的初始化需求,可以留空。
- doFilter(ServletRequest request, ServletResponse response, FilterChain chain),这是过滤器的核心方法,用于执行具体的请求过滤逻辑。你可以在这个方法中检查请求、修改响应,甚至决定是否继续执行过滤链。如果调用了 chain.doFilter(request, response),请求会继续传递到下一个过滤器或目标 Servlet,如果不调用则会阻止请求继续处理。
- destroy() ,它在过滤器销毁时被调用,通常用于释放占用的资源,只执行一次。
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
@Component
public class MyFilter implements Filter {
@Autowired
private MyService myservice;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init MyFilter");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("doFilter before" + myservice.call());
chain.doFilter(request, response);
System.out.println("doFilter after");
}
@Override
public void destroy() {
System.out.println("destroy MyFilter");
}
}
继承 OncePerRequestFilter (推荐)
OncePerRequestFilter 是 Spring Framework 提供的一个过滤器基类,继承自 javax.servlet.Filter 接口,确保每个 HTTP 请求的生命周期内过滤器只执行一次。它通过调用 doFilterInternal() 方法来执行过滤逻辑,并自动避免请求在转发或重定向过程中重复执行过滤器,从而简化了过滤器的实现,避免了冗余操作。OncePerRequestFilter 适用于一些需要避免重复执行的操作,例如日志记录、身份验证等。
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@Component
public class MyFilter extends OncePerRequestFilter {
@Autowired
private MyService myservice;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("doFilter before" + myservice.call());
filterChain.doFilter(request, response);
System.out.println("doFilter after");
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
// 只对 /hello/* 请求生效
return !request.getRequestURI().startsWith("/hello");
}
}
拦截器
在 Spring Boot 中,拦截器需要实现 HandlerInterceptor 接口,并重写三个方法:
- preHandle():请求到达处理方法之前执行,返回 true 表示继续处理请求,返回 false 表示请求被拦截,后续流程停止。
- postHandle():请求处理方法执行后,在视图渲染之前执行,此时可以修改视图对象 ModelAndView。
- afterCompletion():请求处理完成后的回调,即渲染视图完成后,主要用于清理资源。
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Before handler - Request URI: " + request.getRequestURI());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("After handler - Processing request");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("After request completion");
}
}
在 Spring Boot 中,我们可以通过实现 WebMvcConfigurer 接口来设置和注册多个拦截器到 Spring 容器中。WebMvcConfigurer 提供了 addInterceptors 方法,允许我们配置多个拦截器并为每个拦截器设置特定的 URL 模式、排除路径和拦截器执行顺序。
默认情况下,拦截器的执行顺序是按照在 InterceptorRegistry 中的注册顺序决定的 (先注册先执行),但使用 order() 方法,你可以通过显式设置每个拦截器的执行顺序来确保拦截器按照你希望的顺序执行 (数字越小先执行)。
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("/hello/**") // 设置拦截路径
.excludePathPatterns("/api/login", "/api/register") // 设置排除的路径
.order(1); // 设置优先级
registry.addInterceptor(new SecondInterceptor())
.addPathPatterns("/world/**") // 设置拦截路径
.excludePathPatterns("/api/login", "/api/register") // 设置排除的路径
.order(2); // 设置优先级
}
}