Filter与Interceptor深度解析:分清这两个“拦截器”,面试不再掉坑

在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的工作流程可以概括为:

  1. 客户端发送HTTP请求,请求先到达Servlet容器(如Tomcat);

  2. Servlet容器根据web.xml或注解配置,找到匹配该请求的Filter;

  3. 执行Filter的doFilter()方法,在该方法中可以对请求进行预处理(如修改请求头、校验登录状态);

  4. 如果预处理通过,通过FilterChain.doFilter()将请求传递给下一个Filter或目标Servlet;

  5. Servlet处理完请求后,会返回响应,此时会再次回到Filter的doFilter()方法,进行响应的后处理(如修改响应内容、添加响应头);

  6. 最终将处理后的响应返回给客户端。

需要注意的是,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的工作流程如下:

  1. 客户端发送请求,经过Filter过滤后,到达Spring MVC的DispatcherServlet(前端控制器);

  2. DispatcherServlet根据请求路径找到对应的Handler(即Controller方法);

  3. 执行该Handler对应的Interceptor的preHandle()方法(Controller方法执行前);

  4. 如果preHandle()返回true,执行目标Controller方法;如果返回false,拦截请求,不再向下执行;

  5. Controller方法执行完成后,执行Interceptor的postHandle()方法(视图渲染前);

  6. 整个请求处理完成后(视图渲染完成),执行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的场景

  1. 全局请求过滤:如统一设置请求编码(防止乱码)、拦截所有请求进行日志记录;

  2. 静态资源处理:如对CSS、JS、图片等静态资源进行压缩、缓存处理;

  3. 跨域请求处理:实现CORS(跨域资源共享)配置,允许跨域请求;

  4. 脱离Spring的场景:纯Servlet项目,或需要在Spring初始化前执行的逻辑。

优先用Interceptor的场景

  1. Spring生态内的业务拦截:如结合Spring Security进行权限校验、基于用户角色的接口拦截;

  2. Controller方法级别的拦截 :如只拦截带有@RequiresLogin注解的方法;

  3. 需要调用Spring Bean的场景:如拦截时需要调用Service层方法查询用户权限、操作数据库;

  4. 请求处理全流程干预:如在postHandle中修改视图数据,在afterCompletion中清理资源。

特殊场景:二者结合使用

在实际开发中,Filter和Interceptor往往是配合使用的。比如:

  • Filter:负责全局的编码设置、跨域处理、登录状态初步校验;

  • Interceptor:负责具体的业务权限校验(如管理员角色校验)、Controller方法日志记录。

相关推荐
NE_STOP15 分钟前
MyBatis-mybatis入门与增删改查
java
Lee川1 小时前
从异步迷雾到优雅流程:JavaScript异步编程与内存管理的现代化之旅
javascript·面试
晴殇i3 小时前
揭秘JavaScript中那些“不冒泡”的DOM事件
前端·javascript·面试
孟陬3 小时前
国外技术周刊 #1:Paul Graham 重新分享最受欢迎的文章《创作者的品味》、本周被划线最多 YouTube《如何在 19 分钟内学会 AI》、为何我不
java·前端·后端
想用offer打牌4 小时前
一站式了解四种限流算法
java·后端·go
绝无仅有4 小时前
Redis过期删除与内存淘汰策略详解
后端·面试·架构
绝无仅有4 小时前
Redis大Key问题排查与解决方案全解析
后端·面试·架构
华仔啊4 小时前
Java 开发千万别给布尔变量加 is 前缀!很容易背锅
java
AAA梅狸猫5 小时前
Looper.loop() 循环机制
面试