京东Java面试被问:Spring拦截器和过滤器区别

这是一个非常经典的Spring面试题,能很好地区分候选人对Web请求处理流程的理解深度。下面我通过定义、执行顺序、作用范围、应用场景和代码实现五个维度进行详细对比。


一、核心对比一览表

对比维度 过滤器 (Filter) 拦截器 (Interceptor)
所属规范 Servlet 规范 (J2EE标准) Spring 框架 定义
依赖关系 不依赖Spring,可用于任何Web应用 依赖Spring容器
作用范围 过滤所有请求(包括静态资源) 只过滤Spring管理的Controller请求
实现方式 实现 javax.servlet.Filter 接口 实现 HandlerInterceptor 接口
执行位置 Servlet容器(Tomcat/Jetty)层面 Spring MVC框架层面
注入Bean 不能直接注入Spring Bean 可以直接注入Spring Bean
触发时机 在Servlet 之前之后执行 在Controller 方法前后执行

二、执行流程与顺序(最关键的差异)

完整请求处理链

图表

代码

复制

下载

全屏

复制代码
flowchart LR
    A[HTTP请求] --> B[Servlet容器<br>接收请求]
    B --> C[Filter Chain<br>过滤器链]
    
    subgraph C [过滤器执行 - Servlet层面]
        C1[Filter1<br>doFilter]
        C2[Filter2<br>doFilter]
        C3[...]
    end
    
    C --> D[DispatcherServlet<br>Spring MVC前端控制器]
    D --> E[HandlerMapping<br>找到对应Controller]
    E --> F[Interceptor Chain<br>拦截器链]
    
    subgraph F [拦截器执行 - Spring层面]
        direction LR
        F1[PreHandle]
        F2[PreHandle]
        F3[...]
    end
    
    F --> G[Controller<br>执行业务逻辑]
    G --> H[返回ModelAndView]
    
    subgraph I [拦截器后处理]
        I1[PostHandle]
        I2[PostHandle]
        I3[...]
    end
    
    H --> I
    
    I --> J[视图渲染]
    J --> K[Filter Chain<br>过滤器后处理]
    
    subgraph L [拦截器最终处理]
        L1[AfterCompletion]
        L2[AfterCompletion]
        L3[...]
    end
    
    J --> L
    L --> M[HTTP响应]

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

详细执行时序

text

复制

下载

复制代码
客户端请求
    ↓
1. Servlet容器接收请求
    ↓
2. 执行所有 **Filter.doFilter()** 的前置逻辑
    ↓
3. 请求到达 **DispatcherServlet**
    ↓
4. HandlerMapping找到对应Controller
    ↓
5. 执行 **Interceptor.preHandle()** (按配置顺序)
    ↓
6. **Controller方法执行**
    ↓
7. 执行 **Interceptor.postHandle()** (按配置逆序)
    ↓
8. 视图渲染(如JSP、Thymeleaf)
    ↓
9. 执行 **Interceptor.afterCompletion()** (按配置逆序)
    ↓
10. 执行所有 **Filter.doFilter()** 的后置逻辑
    ↓
11. 返回响应给客户端

关键记忆点

  • Filter包裹Interceptor:Filter是最外层,Interceptor在内层

  • Controller只在最中心:Interceptor包裹着Controller方法

  • 逆序执行:postHandle和afterCompletion是逆序的


三、代码实现对比

过滤器 (Filter) 实现

java

复制

下载

复制代码
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

// 方式1:注解配置(Servlet 3.0+)
@WebFilter(urlPatterns = "/*", filterName = "logFilter")
// 方式2:通过FilterRegistrationBean配置(Spring Boot推荐)
public class LogFilter implements Filter {
    
    @Override
    public void init(FilterConfig filterConfig) {
        // 初始化方法,容器启动时调用一次
        System.out.println("LogFilter初始化");
    }
    
    @Override
    public void doFilter(ServletRequest request, 
                        ServletResponse response, 
                        FilterChain chain) 
            throws IOException, ServletException {
        
        long startTime = System.currentTimeMillis();
        System.out.println("Filter开始: " + 
                          ((HttpServletRequest)request).getRequestURI());
        
        // 1. 前置处理(Controller之前)
        // 可以修改Request/Response,如设置字符编码
        request.setCharacterEncoding("UTF-8");
        
        // 放行到下一个过滤器或Servlet
        chain.doFilter(request, response);
        
        // 2. 后置处理(Controller之后)
        long endTime = System.currentTimeMillis();
        System.out.println("Filter结束,耗时: " + (endTime - startTime) + "ms");
    }
    
    @Override
    public void destroy() {
        // 销毁方法,容器关闭时调用
        System.out.println("LogFilter销毁");
    }
}

// Spring Boot配置方式(推荐)
@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<LogFilter> logFilter() {
        FilterRegistrationBean<LogFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new LogFilter());
        bean.addUrlPatterns("/*");
        bean.setOrder(1); // 设置执行顺序
        bean.setName("logFilter");
        return bean;
    }
}

拦截器 (Interceptor) 实现

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
public class AuthInterceptor implements HandlerInterceptor {
    
    // 注入Spring Bean(过滤器不能直接注入)
    @Autowired
    private UserService userService;
    
    /**
     * Controller方法执行前调用
     * 返回值:true-继续执行,false-中断执行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, 
                            HttpServletResponse response, 
                            Object handler) throws Exception {
        
        System.out.println("Interceptor preHandle: " + request.getRequestURI());
        
        // 示例:权限校验
        String token = request.getHeader("Authorization");
        if (!userService.validateToken(token)) {
            response.setStatus(401);
            response.getWriter().write("Unauthorized");
            return false; // 中断请求,不执行Controller
        }
        
        // 设置请求属性,Controller中可以获取
        request.setAttribute("startTime", System.currentTimeMillis());
        return true; // 继续执行
    }
    
    /**
     * Controller方法执行后,视图渲染前调用
     * 可以修改ModelAndView
     */
    @Override
    public void postHandle(HttpServletRequest request, 
                          HttpServletResponse response, 
                          Object handler,
                          ModelAndView modelAndView) throws Exception {
        
        System.out.println("Interceptor postHandle");
        
        if (modelAndView != null) {
            // 可以往Model中添加全局数据
            modelAndView.addObject("serverTime", System.currentTimeMillis());
        }
    }
    
    /**
     * 整个请求完成后调用(视图渲染完毕)
     * 适合进行资源清理、异常日志记录
     */
    @Override
    public void afterCompletion(HttpServletRequest request, 
                               HttpServletResponse response, 
                               Object handler, 
                               Exception ex) throws Exception {
        
        long startTime = (Long) request.getAttribute("startTime");
        long endTime = System.currentTimeMillis();
        System.out.println("请求处理完成,耗时: " + (endTime - startTime) + "ms");
        
        if (ex != null) {
            // 记录异常日志
            System.err.println("请求异常: " + ex.getMessage());
        }
    }
}

// 注册拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    @Autowired
    private AuthInterceptor authInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/api/**")   // 拦截路径
                .excludePathPatterns("/api/login", "/api/register") // 排除路径
                .order(1); // 执行顺序
    }
}

四、应用场景选择指南

使用过滤器的场景

  1. 全局字符编码处理

    java

    复制

    下载

    复制代码
    // 在过滤器中统一设置UTF-8编码
    request.setCharacterEncoding("UTF-8");
    response.setCharacterEncoding("UTF-8");
  2. 跨域请求处理 (CORS)

    java

    复制

    下载

    复制代码
    // 添加CORS头
    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Methods", "*");
  3. XSS/SQL注入防护

    java

    复制

    下载

    复制代码
    // 对请求参数进行全局过滤
    XssHttpServletRequestWrapper wrappedRequest = 
        new XssHttpServletRequestWrapper(request);
    chain.doFilter(wrappedRequest, response);
  4. 静态资源缓存控制

    java

    复制

    下载

    复制代码
    if (request.getRequestURI().endsWith(".js")) {
        response.setHeader("Cache-Control", "max-age=31536000");
    }
  5. 请求/响应日志记录(所有请求,包括静态资源)

使用拦截器的场景

  1. 用户身份认证与授权

    java

    复制

    下载

    复制代码
    // 检查Session或Token
    if (request.getSession().getAttribute("user") == null) {
        response.sendRedirect("/login");
        return false;
    }
  2. 接口访问频率限制

    java

    复制

    下载

    复制代码
    // 基于IP或用户ID的限流
    String key = "rate_limit:" + getClientIp(request);
    if (redisTemplate.opsForValue().increment(key) > 100) {
        return false; // 超过限制
    }
  3. Controller方法执行时间监控

    java

    复制

    下载

    复制代码
    // 在preHandle记录开始时间
    // 在afterCompletion计算耗时并上报监控系统
  4. 统一异常处理增强

    java

    复制

    下载

    复制代码
    // 在afterCompletion中记录异常到日志系统
  5. 业务相关的预处理

    java

    复制

    下载

    复制代码
    // 如:解析设备类型、设置时区、封装用户上下文等

实际项目中的典型配置

java

复制

下载

复制代码
@Configuration
public class WebConfig {
    /**
     * 过滤器配置:处理底层、通用的Web请求
     */
    @Bean
    public FilterRegistrationBean<CharacterEncodingFilter> encodingFilter() {
        FilterRegistrationBean<CharacterEncodingFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new CharacterEncodingFilter("UTF-8", true));
        bean.addUrlPatterns("/*");
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 最高优先级
        return bean;
    }
    
    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        // CORS过滤器配置
        return new FilterRegistrationBean<>(new CorsFilter());
    }
    
    /**
     * 拦截器配置:处理业务相关的请求
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 1. 日志拦截器(最外层)
        registry.addInterceptor(new LogInterceptor())
                .addPathPatterns("/**")
                .order(1);
        
        // 2. 认证拦截器
        registry.addInterceptor(new AuthInterceptor())
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/public/**")
                .order(2);
        
        // 3. 性能监控拦截器(最内层)
        registry.addInterceptor(new PerformanceInterceptor())
                .addPathPatterns("/api/**")
                .order(3);
    }
}

五、面试深度回答要点

基础回答(校招/初级)

"过滤器是Servlet规范定义的,在Servlet容器层面工作,可以过滤所有请求包括静态资源;拦截器是Spring框架定义的,在Spring MVC层面工作,只能拦截Controller请求。过滤器通过doFilter方法,拦截器通过preHandle、postHandle、afterCompletion三个方法。"

进阶回答(社招/中级)

"从执行顺序看,过滤器在最外层,拦截器在内层。过滤器可以修改Request/Response,拦截器可以获取到Handler和ModelAndView。过滤器不能注入Spring Bean,拦截器可以。过滤器适合做全局的、与业务无关的处理(如编码、CORS),拦截器适合做业务相关的处理(如权限、日志)。"

深度回答(高级/架构师)

"这本质上是责任链模式在不同层面的实现。过滤器是Servlet容器的责任链,拦截器是Spring MVC的责任链。在设计上,过滤器关注的是HTTP请求/响应的原始处理,拦截器关注的是MVC流程中的业务切面。生产环境中,我们通常用过滤器处理基础设施层的问题(安全、编码、压缩),用拦截器处理应用层的问题(认证、审计、性能监控)。对于微服务架构,许多过滤器的功能可以下沉到API网关。"

扩展问题准备

  1. 如何保证多个过滤器/拦截器的执行顺序?

    • 过滤器:通过FilterRegistrationBean.setOrder()

    • 拦截器:通过InterceptorRegistry.addInterceptor().order()

  2. 拦截器preHandle返回false后,postHandle还会执行吗?

    • 不会。preHandle返回false会中断整个执行链。
  3. 过滤器中抛出异常会怎样?

    • 如果没有异常处理,会返回500错误,后续过滤器和拦截器不会执行。
  4. 可以同时修改Request和Response吗?

    • 过滤器可以,拦截器主要修改Response,修改Request需要通过包装类。
  5. Spring Boot中如何禁用默认过滤器?

    properties

    复制

    下载

    复制代码
    # 禁用Spring Security的默认过滤器
    security.basic.enabled=false
    # 或通过配置类排除
    @ComponentScan(excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, 
                                          classes = {SomeFilter.class}))

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

掌握这个区别不仅为了面试,更重要的是在实际项目中能够合理设计请求处理链,构建清晰、可维护的Web应用架构。

相关推荐
2401_871260023 小时前
Java学习笔记(二)面向对象
java·python·学习
是梦终空3 小时前
计算机毕业设计252—基于Java+Springboot+vue3+协同过滤推荐算法的农产品销售系统(源代码+数据库+2万字论文)
java·spring boot·vue·毕业设计·源代码·协同过滤算法·农产品销售系统
丿BAIKAL巛3 小时前
Java前后端传参与接收全解析
java·开发语言
cc蒲公英3 小时前
javascript有哪些内置对象
java·前端·javascript
guslegend3 小时前
Spring AOP高级应用与源码剖析
java
Rover.x3 小时前
head table is mandatory
java·apache
yanghuashuiyue3 小时前
Java过滤器-拦截器-AOP-Controller
java·开发语言
shoubepatien4 小时前
JAVA —— 03
java·jvm
a努力。4 小时前
【基础数据篇】数据等价裁判:Comparer模式
java·后端