在Java Web开发和Spring框架中,Filter(过滤器)和Interceptor(拦截器)是两个高频出现的组件------它们都能实现"请求拦截"功能,比如登录校验、日志记录、权限控制等,因此很多新手会把二者混淆。
但实际上,Filter和Interceptor在底层原理、适用范围、执行时机等方面有着本质区别。今天这篇文章,咱们就从"是什么""怎么用""有啥不同"三个层面,把这两个组件彻底讲透,帮你建立清晰的认知,下次开发和面试都能游刃有余。
一、先搞懂:Filter与Interceptor的核心定位
在正式拆解前,我们先明确一个核心结论:Filter是Java EE规范的组件,属于"Servlet容器级"的拦截;Interceptor是Spring框架的组件,属于"Spring上下文级"的拦截。
简单来说,Filter不依赖于Spring,只要是Java Web项目(哪怕不用Spring)都能使用;而Interceptor是Spring的"专属工具",必须在Spring环境中才能工作,并且能深度结合Spring的IOC容器、AOP等特性。
举个生活化的例子:如果把HTTP请求比作"快递",那么Servlet容器就是"小区快递站",Spring上下文就是"你家单元楼"。Filter是快递站的"安检员",所有进入快递站的快递(不管是不是送到你家的)都要经过它检查;Interceptor是单元楼的"门禁管理员",只负责检查送到本单元楼的快递,而且还能知道快递是送给哪一户的(对应Spring的Bean信息)。

二、Servlet容器的"安检员":Filter过滤器
2.1 核心原理:基于Servlet规范的请求拦截
Filter是Java EE定义在javax.servlet包下的接口,其核心作用是在HTTP请求到达Servlet(或JSP)之前,以及响应返回给客户端之前,对请求和响应进行预处理和后处理。
Filter的工作流程可以概括为:
-
客户端发送HTTP请求,请求先到达Servlet容器(如Tomcat);
-
Servlet容器根据web.xml或注解配置,找到匹配该请求的Filter;
-
执行Filter的
doFilter()方法,在该方法中可以对请求进行预处理(如修改请求头、校验登录状态); -
如果预处理通过,通过
FilterChain.doFilter()将请求传递给下一个Filter或目标Servlet; -
Servlet处理完请求后,会返回响应,此时会再次回到Filter的
doFilter()方法,进行响应的后处理(如修改响应内容、添加响应头); -
最终将处理后的响应返回给客户端。
需要注意的是,Filter是"链式执行"的,如果配置了多个Filter,会按照配置顺序依次执行(一般是按照按照过滤器类名(字符串)的自然排序的顺序),形成一个过滤链。
2.2 实战:手写一个登录校验Filter
光说原理不够直观,咱们通过一个常见场景------"登录校验"来手写Filter,看看它具体怎么用。
步骤1:创建Filter实现类
实现javax.servlet.Filter接口,重写init()(初始化)、doFilter()(核心拦截逻辑)、destroy()(销毁)方法。其中doFilter()是核心。
java
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 注解配置:拦截所有请求(/*)
@WebFilter("/*")
public class LoginCheckFilter implements Filter {
// 初始化方法,Filter创建时执行(仅一次)
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("LoginCheckFilter初始化...");
}
// 核心拦截方法,每次请求匹配时执行
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 1. 转换请求响应对象(因为ServletRequest是顶层接口,需要转成HTTP专属的实现类)
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 2. 获取请求路径
String requestURI = request.getRequestURI();
// 3. 排除白名单(登录、注册接口不需要拦截)
if (requestURI.contains("/login") || requestURI.contains("/register")) {
// 直接放行
filterChain.doFilter(request, response);
return;
}
// 4. 校验登录状态:从Session中获取用户信息
Object user = request.getSession().getAttribute("user");
if (user != null) {
// 已登录,放行
filterChain.doFilter(request, response);
return;
}
// 5. 未登录,返回错误响应
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\":401,\"msg\":\"请先登录!\"}");
}
// 销毁方法,Filter销毁时执行(仅一次,如服务器关闭)
@Override
public void destroy() {
System.out.println("LoginCheckFilter销毁...");
}
}
步骤2:开启Filter扫描
在Spring Boot启动类上添加@ServletComponentScan注解,让Spring扫描到我们用@WebFilter注解配置的Filter。
java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan // 扫描Servlet相关组件(Filter、Servlet、Listener)
public class FilterInterceptorDemoApplication {
public static void main(String[] args) {
SpringApplication.run(FilterInterceptorDemoApplication.class, args);
}
}
步骤3:测试效果
-
访问
/login:直接放行,正常返回登录页面; -
访问
/user/info(需要登录的接口):未登录时返回{"code":401,"msg":"请先登录!"},登录后正常返回用户信息。
2.3 Filter的核心特点
-
依赖Servlet容器:必须在Tomcat等Servlet容器中运行,脱离容器无法工作;
-
拦截范围广:可以拦截所有HTTP请求,包括静态资源(如CSS、JS、图片)和动态请求(如Servlet、Controller);
-
基于请求路径匹配:通过请求URI进行匹配,无法精确到Java方法级别;
-
可操作请求和响应对象:可以修改请求头、请求参数,也可以修改响应内容、响应头;
-
不依赖Spring:即使不用Spring,纯Servlet项目也能使用。
三、Spring的"门禁管理员":Interceptor拦截器
3.1 核心原理:基于Spring AOP的方法拦截
Interceptor是Spring框架定义在org.springframework.web.servlet包下的接口,其核心作用是在Spring MVC的Controller方法执行前后,以及视图渲染前后,对请求进行拦截和处理。
和Filter不同,Interceptor是基于Spring AOP实现的,它依赖于Spring的IOC容器,能够获取到Spring管理的Bean,因此可以实现更灵活的业务逻辑(如调用Service层方法进行权限校验)。
Interceptor的工作流程如下:
-
客户端发送请求,经过Filter过滤后,到达Spring MVC的DispatcherServlet(前端控制器);
-
DispatcherServlet根据请求路径找到对应的Handler(即Controller方法);
-
执行该Handler对应的Interceptor的
preHandle()方法(Controller方法执行前); -
如果
preHandle()返回true,执行目标Controller方法;如果返回false,拦截请求,不再向下执行; -
Controller方法执行完成后,执行Interceptor的
postHandle()方法(视图渲染前); -
整个请求处理完成后(视图渲染完成),执行Interceptor的
afterCompletion()方法(用于资源清理等)。
3.2 实战:手写一个权限校验Interceptor
我们还是以"权限校验"为场景,手写一个Interceptor------只有拥有"ADMIN"角色的用户才能访问管理员接口。
步骤1:创建Interceptor实现类
实现HandlerInterceptor接口,重写三个核心方法:preHandle()、postHandle()、afterCompletion()。
java
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component // 交给Spring管理
public class AdminPermissionInterceptor implements HandlerInterceptor {
/**
* Controller方法执行前执行
* @return true:放行;false:拦截
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("AdminPermissionInterceptor:preHandle(Controller方法执行前)");
// 1. 从Session获取用户角色
String role = (String) request.getSession().getAttribute("role");
// 2. 校验角色(仅ADMIN可访问)
if ("ADMIN".equals(role)) {
return true; // 权限通过,放行
}
// 3. 权限不足,返回错误响应
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\":403,\"msg\":\"权限不足,仅管理员可访问!\"}");
return false;
}
/**
* Controller方法执行后,视图渲染前执行
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("AdminPermissionInterceptor:postHandle(视图渲染前)");
// 可在这里修改视图数据,如给modelAndView添加公共参数
}
/**
* 整个请求处理完成后执行(视图渲染完成)
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("AdminPermissionInterceptor:afterCompletion(请求处理完成)");
// 可在这里进行资源清理,如关闭流、释放连接等
}
}
步骤2:配置Interceptor
通过WebMvcConfigurer接口的addInterceptors()方法,配置Interceptor的拦截路径和排除路径。
java
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
@Configuration // 标识为配置类
public class WebMvcConfig implements WebMvcConfigurer {
@Resource // 注入自定义的Interceptor
private AdminPermissionInterceptor adminPermissionInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(adminPermissionInterceptor)
.addPathPatterns("/admin/**") // 拦截/admin开头的所有请求
.excludePathPatterns("/admin/login"); // 排除/admin/login接口(管理员登录不需要权限校验)
}
}
步骤3:测试效果
-
角色为ADMIN的用户访问
/admin/user/list:正常返回用户列表; -
角色为USER的用户访问
/admin/user/list:返回{"code":403,"msg":"权限不足,仅管理员可访问!"}; -
访问
/admin/login:被排除拦截,正常返回登录页面。
3.3 Interceptor的核心特点
-
依赖Spring环境:必须在Spring(或Spring Boot)项目中使用,由Spring IOC容器管理;
-
拦截范围精准:主要拦截Spring MVC的Controller方法,不拦截静态资源(除非特殊配置),可通过注解精准到方法级别;
-
可获取Spring Bean:能直接注入Service、Mapper等Bean,实现复杂业务逻辑;
-
多阶段拦截:提供preHandle、postHandle、afterCompletion三个方法,覆盖请求处理的全流程;
-
基于Handler匹配:可以根据Controller的方法、注解等进行匹配,比Filter的路径匹配更灵活。
在拦截器中除了可以设置/**拦截所有资源外,还有一些常见拦截路径设置:

四、灵魂对比:Filter与Interceptor的8大核心差异
很多面试都会问"Filter和Interceptor有什么区别",这里我们用一张表把核心差异总结清楚,方便大家记忆:

五:什么时候用Filter?什么时候用Interceptor?
技术没有绝对的好坏,只有适合与否。结合二者的特点,我们给出明确的选型建议:
优先用Filter的场景
-
全局请求过滤:如统一设置请求编码(防止乱码)、拦截所有请求进行日志记录;
-
静态资源处理:如对CSS、JS、图片等静态资源进行压缩、缓存处理;
-
跨域请求处理:实现CORS(跨域资源共享)配置,允许跨域请求;
-
脱离Spring的场景:纯Servlet项目,或需要在Spring初始化前执行的逻辑。
优先用Interceptor的场景
-
Spring生态内的业务拦截:如结合Spring Security进行权限校验、基于用户角色的接口拦截;
-
Controller方法级别的拦截 :如只拦截带有
@RequiresLogin注解的方法; -
需要调用Spring Bean的场景:如拦截时需要调用Service层方法查询用户权限、操作数据库;
-
请求处理全流程干预:如在postHandle中修改视图数据,在afterCompletion中清理资源。
特殊场景:二者结合使用
在实际开发中,Filter和Interceptor往往是配合使用的。比如:
-
Filter:负责全局的编码设置、跨域处理、登录状态初步校验;
-
Interceptor:负责具体的业务权限校验(如管理员角色校验)、Controller方法日志记录。