沉梦手记 沉梦手记
首页
  • 基础篇
  • 集合篇
  • 并发篇
  • JVM
  • 新特性
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 基础篇
  • MySql
  • Redis
  • 达梦数据库
  • Spring
  • SpringBoot
  • Mybatis
  • Shiro
  • 设计须知
  • UML画图
  • 权限校验
  • 设计模式
  • API网关
  • 网络通信
  • 消息队列
  • SpringCloud
  • 分布式事务
  • 云存储
  • 搜索引擎
  • 音视频处理
  • Linux 与容器化运维
  • 开发工具篇
  • 工具库篇
  • 开发技巧篇
  • 工具类系列
  • 随笔
  • 前端环境搭建
  • HTML与CSS
  • JS学习
  • Axios入门
  • Vue Router入门
  • Pinia入门
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • 脚手架搭建
  • 瑞吉外卖
  • 黑马点评
  • vue-blog
  • 沉梦接口开放平台
  • 用户中心
  • 聚合搜索平台
  • 仿12306项目
  • 壁纸小程序项目
  • RuoYi-Vue
  • 博客搭建
  • 网站收藏箱
  • 断墨寻径摘录
  • 费曼学习法
Github (opens new window)

沉梦听雨

时间是最好的浸渍剂,而沉淀是最好的提纯器🚀
首页
  • 基础篇
  • 集合篇
  • 并发篇
  • JVM
  • 新特性
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 基础篇
  • MySql
  • Redis
  • 达梦数据库
  • Spring
  • SpringBoot
  • Mybatis
  • Shiro
  • 设计须知
  • UML画图
  • 权限校验
  • 设计模式
  • API网关
  • 网络通信
  • 消息队列
  • SpringCloud
  • 分布式事务
  • 云存储
  • 搜索引擎
  • 音视频处理
  • Linux 与容器化运维
  • 开发工具篇
  • 工具库篇
  • 开发技巧篇
  • 工具类系列
  • 随笔
  • 前端环境搭建
  • HTML与CSS
  • JS学习
  • Axios入门
  • Vue Router入门
  • Pinia入门
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • 脚手架搭建
  • 瑞吉外卖
  • 黑马点评
  • vue-blog
  • 沉梦接口开放平台
  • 用户中心
  • 聚合搜索平台
  • 仿12306项目
  • 壁纸小程序项目
  • RuoYi-Vue
  • 博客搭建
  • 网站收藏箱
  • 断墨寻径摘录
  • 费曼学习法
Github (opens new window)
  • 设计须知

    • 命名规范
    • 聊聊什么是耦合度
    • 幂等性问题分析
    • LocalDateTime和DateTime
    • 聊聊软件架构设计
    • 说说war和jar的区别
    • 聊聊多租户是什么
    • 加密算法分析
    • 那些关于管理系统的知识
    • 唯一索引和逻辑删除冲突解决方法
    • 日志记录相关
    • 过滤器、拦截器、AOP切面与其他拦截机制
      • 概述
      • 过滤器(Filter)
        • 基本概念
        • 核心方法
        • 注册方式
        • 方式一:@WebFilter 注解 + @ServletComponentScan
        • 方式二:FilterRegistrationBean(推荐)
        • 常见使用场景
      • 拦截器(Interceptor)
        • 基本概念
        • 核心方法
        • 注册方式
        • 三个方法的执行时机
        • 常见使用场景
      • AOP 切面(Aspect)
        • 增加AOP切面
        • 核心注解
        • 代码示例
        • 切入点表达式
        • 常见使用场景
      • 全局异常处理器(@RestControllerAdvice)
        • 基本概念
        • 核心注解
        • 代码示例
        • 异常处理流程
        • 异常匹配规则
        • 与 Filter / Interceptor / AOP 的关系
        • 常见使用场景
      • 跨域配置(CORS)
        • 基本概念
        • 三种实现方式
        • 方式一:@CrossOrigin 注解(粒度最细)
        • 方式二:WebMvcConfigurer.addCorsMappings(全局配置)
        • 方式三:CorsFilter(推荐,最灵活)
        • 三种方式对比
        • CORS 预检请求(Preflight)
      • 其他拦截 / 捕获机制
        • ResponseBodyAdvice(响应体增强)
        • RequestBodyAdvice(请求体增强)
        • ErrorController(兜底异常处理)
        • Spring Security Filter Chain(安全过滤器链)
        • Spring Cloud Gateway GlobalFilter(网关层过滤器)
        • HandlerExceptionResolver(异常解析器)
        • 各机制总览
      • 各机制对比
      • 执行顺序详解
        • 完整执行流程
        • 简化记忆
        • 多个 Filter / Interceptor 的顺序
        • 多个 Filter
        • 多个 Interceptor
        • 验证执行顺序的示例
      • 异常情况下的执行顺序
        • Controller 抛出异常
        • preHandle 返回 false
        • 参数解析异常
      • 如何选择?
        • 决策流程
        • 场景速查
      • 常见问题
        • 1. Filter 中无法注入 Bean?
        • 2. Interceptor 中无法注入 Bean?
        • 3. AOP 不生效?
        • 4. Filter 和 Interceptor 都能做登录校验,选哪个?
      • 总结
    • 缓存设计分析
  • UML画图

  • 权限校验

  • 设计模式

  • 系统设计
  • 设计须知
沉梦听雨
2026-08-13
目录

过滤器、拦截器、AOP切面与其他拦截机制

# 过滤器、拦截器、AOP切面与其他拦截机制

# 概述

在 Java Web 开发中,常用的请求拦截与横切处理机制包括:

  • 过滤器(Filter) — Servlet 容器层
  • 拦截器(Interceptor) — Spring MVC 层
  • AOP 切面(Aspect) — Spring Bean 方法层
  • 全局异常处理器(@RestControllerAdvice) — Controller 异常捕获层
  • 跨域配置(CORS) — 跨域请求处理
  • 其他机制 — ResponseBodyAdvice、ErrorController、Spring Security Filter Chain 等

它们都能在业务逻辑执行前后插入额外处理,但作用层级、触发时机、适用场景各不相同。

一句话总结:Filter 管最外层,Interceptor 管 Controller 层,AOP 管任意 Bean 方法层,@RestControllerAdvice 管异常兜底。

整体关系示意:

HTTP 请求
    │
    ▼
┌──────────────────────────────────────────────────┐
│  Spring Security Filter Chain(安全过滤器链)      │
│  ┌────────────────────────────────────────────┐  │
│  │  Filter(Servlet 容器层)                    │  │
│  │  ┌──────────────────────────────────────┐  │  │
│  │  │  DispatcherServlet                    │  │  │
│  │  │  ┌────────────────────────────────┐  │  │  │
│  │  │  │  Interceptor(Spring MVC)       │  │  │  │
│  │  │  │  ┌──────────────────────────┐   │  │  │  │
│  │  │  │  │  Controller               │   │  │  │  │
│  │  │  │  │  ┌────────────────────┐   │   │  │  │  │
│  │  │  │  │  │  AOP(Spring)      │   │   │  │  │  │
│  │  │  │  │  │  ┌──────────────┐   │   │   │  │  │  │
│  │  │  │  │  │  │  Service     │   │   │   │  │  │  │
│  │  │  │  │  │  └──────────────┘   │   │   │  │  │  │
│  │  │  │  │  └────────────────────┘   │   │  │  │  │
│  │  │  │  └──────────────────────────┘   │  │  │  │
│  │  │  │  ┌──────────────────────────┐   │  │  │  │
│  │  │  │  │  @RestControllerAdvice    │   │  │  │  │  ← 异常捕获
│  │  │  │  │  ResponseBodyAdvice       │   │  │  │  │  ← 响应体包装
│  │  │  │  └──────────────────────────┘   │  │  │  │
│  │  │  └────────────────────────────────┘  │  │  │
│  │  └──────────────────────────────────────┘  │  │
│  └────────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────────┐  │
│  │  ErrorController(最后防线)                 │  │
│  └────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────┘
    │
    ▼
HTTP 响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 过滤器(Filter)

# 基本概念

过滤器是 Servlet 规范中的组件(javax.servlet.Filter / jakarta.servlet.Filter),运行在 Servlet 容器层,在请求到达 DispatcherServlet 之前和响应离开 DispatcherServlet 之后执行。

  • 依赖于 Servlet 容器(如 Tomcat)
  • 可以拦截所有请求(包括静态资源、JSP 等)
  • 基于回调函数实现

# 核心方法

public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 过滤器初始化,应用启动时执行一次
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 请求处理前逻辑
        System.out.println("Filter - 请求前");

        // 放行,将请求传递给下一个 Filter 或 Servlet
        chain.doFilter(request, response);

        // 响应处理逻辑
        System.out.println("Filter - 响应后");
    }

    @Override
    public void destroy() {
        // 过滤器销毁,应用关闭时执行一次
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 注册方式

# 方式一:@WebFilter 注解 + @ServletComponentScan

@WebFilter(urlPatterns = "/*", filterName = "myFilter")
public class MyFilter implements Filter {
    // ...
}

// 启动类上添加扫描注解
@ServletComponentScan
@SpringBootApplication
public class Application { }
1
2
3
4
5
6
7
8
9

# 方式二:FilterRegistrationBean(推荐)

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<MyFilter> myFilterRegistration() {
        FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new MyFilter());
        registration.addUrlPatterns("/*");
        registration.setName("myFilter");
        registration.setOrder(1); // 数字越小,优先级越高
        return registration;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 常见使用场景

场景 说明
字符编码设置 统一设置 UTF-8 编码
CORS 跨域处理 添加跨域响应头
请求日志记录 记录请求 URL、参数、耗时等
请求/响应包装 如对请求体进行缓存(ContentCachingRequestWrapper)
安全过滤 如 XSS 过滤、SQL 注入过滤
全局异常过滤 在最外层捕获未处理的异常

# 拦截器(Interceptor)

# 基本概念

拦截器是 Spring MVC 框架中的组件(HandlerInterceptor),运行在 DispatcherServlet 内部,在 Controller 方法执行前后介入。

  • 依赖于 Spring MVC 框架
  • 只能拦截进入 Controller 的请求(静态资源等不经过拦截器)
  • 基于 Java 反射机制实现

# 核心方法

public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        // Controller 方法执行前调用
        // 返回 true:放行;返回 false:中断请求
        System.out.println("Interceptor - preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) throws Exception {
        // Controller 方法执行后、视图渲染前调用
        System.out.println("Interceptor - postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
        // 视图渲染完成后调用(无论是否抛出异常)
        System.out.println("Interceptor - afterCompletion");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 注册方式

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/**")       // 拦截路径
                .excludePathPatterns("/login", "/static/**"); // 排除路径
    }
}
1
2
3
4
5
6
7
8
9
10

# 三个方法的执行时机

请求 → preHandle → Controller → postHandle → 视图渲染 → afterCompletion
1
方法 执行时机 异常时是否执行
preHandle Controller 执行前 ✅ 执行
postHandle Controller 执行后、渲染前 ❌ 不执行(Controller 抛异常时跳过)
afterCompletion 视图渲染完成后 ✅ 执行

⚠️ 注意:如果 preHandle 返回 false,后续拦截器和 Controller 都不会执行,但前面已执行的拦截器的 afterCompletion 仍会被调用。

# 常见使用场景

场景 说明
登录校验 检查 Token / Session 是否有效
权限校验 检查用户是否有访问权限
日志记录 记录请求和响应信息
性能监控 统计接口耗时
请求参数预处理 统一添加或修改请求参数

💡 项目中已有使用拦截器验证 Bearer Token 的示例,可参考 深入了解 Bearer 模式。

# AOP 切面(Aspect)

# 增加AOP切面

AOP(面向切面编程)是 Spring 框架的核心特性之一,通过动态代理在Bean 方法级别实现横切逻辑。

  • 作用于 Spring 容器管理的 Bean
  • 基于动态代理(JDK 代理 / CGLIB 代理)实现
  • 粒度更细,可以精确到任意方法

关于 AOP 的基础概念和常见注解,可参考 AOP 实战 和 Spring IoC 和 AOP。

# 核心注解

注解 说明
@Aspect 声明切面类
@Pointcut 定义切入点(匹配哪些方法)
@Before 前置通知:方法执行前
@After 后置通知:方法执行后(无论是否异常)
@AfterReturning 返回通知:方法正常返回后
@AfterThrowing 异常通知:方法抛出异常后
@Around 环绕通知:方法执行前后(最灵活)

# 代码示例

@Aspect
@Component
public class LogAspect {

    // 定义切入点:匹配 service 包下所有方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void servicePointcut() {}

    // 前置通知
    @Before("servicePointcut()")
    public void before(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("AOP - Before: " + methodName + ", args=" + Arrays.toString(args));
    }

    // 环绕通知(最常用)
    @Around("servicePointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();

        // 执行前
        String methodName = joinPoint.getSignature().getName();
        System.out.println("AOP - Around前: " + methodName);

        Object result;
        try {
            // 执行目标方法
            result = joinPoint.proceed();
        } catch (Throwable e) {
            // 异常处理
            System.out.println("AOP - Around异常: " + e.getMessage());
            throw e;
        }

        // 执行后
        long cost = System.currentTimeMillis() - start;
        System.out.println("AOP - Around后: " + methodName + ", 耗时=" + cost + "ms");

        return result;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 切入点表达式

表达式 匹配范围
execution(* com.example.service.*.*(..)) service 包下所有类的所有方法
execution(* com.example..*.*(..)) com.example 及其子包下所有方法
@annotation(com.example.MyAnnotation) 标注了 @MyAnnotation 的方法
@within(com.example.MyAnnotation) 标注了 @MyAnnotation 的类的所有方法
bean(userService) 名称为 userService 的 Bean 的所有方法
within(com.example.controller.*) controller 包下所有类的所有方法

# 常见使用场景

场景 说明
日志记录 记录方法调用参数、返回值、耗时
权限校验 自定义注解 + AOP 实现细粒度权限控制
事务管理 @Transactional 底层就是通过 AOP 实现的
缓存 @Cacheable 等注解基于 AOP
接口限流 自定义注解 + AOP + Redis 实现限流
参数校验 方法执行前校验参数合法性

# 全局异常处理器(@RestControllerAdvice)

# 基本概念

@RestControllerAdvice 是 Spring 提供的全局异常处理组件,本质是 @ControllerAdvice + @ResponseBody 的组合。它用于统一捕获 Controller 层抛出的异常,并将其转换为标准化的 JSON 响应。

  • 作用于 Controller 层(不拦截 Filter / Interceptor 中的异常)
  • 基于 HandlerExceptionResolver 机制实现
  • 异常被捕获后,不会再传播到 Interceptor 的 postHandle(这也是 postHandle 在异常时不执行的原因)

💡 @RestControllerAdvice 捕获异常后,Interceptor.afterCompletion 仍会执行,且 ex 参数为 null(因为异常已被处理)。

# 核心注解

注解 说明
@RestControllerAdvice 声明全局异常处理类(返回 JSON)
@ExceptionHandler 声明处理某种异常的方法
@ControllerAdvice 同上,但需配合 @ResponseBody 才返回 JSON

# 代码示例

@RestControllerAdvice
public class GlobalExceptionHandler {

    // 处理业务异常
    @ExceptionHandler(BusinessException.class)
    public Result<Void> handleBusinessException(BusinessException e) {
        log.error("业务异常: {}", e.getMessage(), e);
        return Result.fail(e.getCode(), e.getMessage());
    }

    // 处理参数校验异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Void> handleValidException(MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getFieldErrors().stream()
                .map(error -> error.getField() + ": " + error.getDefaultMessage())
                .collect(Collectors.joining("; "));
        return Result.fail(400, message);
    }

    // 处理所有未捕获的异常(兜底)
    @ExceptionHandler(Exception.class)
    public Result<Void> handleException(Exception e) {
        log.error("系统异常: {}", e.getMessage(), e);
        return Result.fail(500, "系统内部错误");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 异常处理流程

Controller 抛出异常
    │
    ▼
DispatcherServlet 捕获异常
    │
    ▼
HandlerExceptionResolver 解析
    │
    ▼
@ExceptionHandler 匹配异常类型
    │
    ├── 匹配成功 → 执行处理方法 → 返回 JSON 响应
    │
    └── 匹配失败 → 异常继续传播 → ErrorController 兜底
    │
    ▼
Interceptor.afterCompletion(ex=null,异常已被处理)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 异常匹配规则

@ExceptionHandler 按异常类型的最近父类匹配:

// 定义异常继承链
class BusinessException extends RuntimeException {}
class OrderNotFoundException extends BusinessException {}

// 处理器
@ExceptionHandler(OrderNotFoundException.class)  // 精确匹配,优先级最高
public Result<?> handleOrderNotFound(OrderNotFoundException e) { ... }

@ExceptionHandler(BusinessException.class)      // 父类匹配,次之
public Result<?> handleBusiness(BusinessException e) { ... }

@ExceptionHandler(Exception.class)              // 兜底,优先级最低
public Result<?> handleAll(Exception e) { ... }
1
2
3
4
5
6
7
8
9
10
11
12
13

⚠️ 如果有多个 @RestControllerAdvice,可通过 @Order 控制优先级,数字越小优先级越高。

# 与 Filter / Interceptor / AOP 的关系

组件 能否捕获异常 说明
Filter ❌ 不能被 @RestControllerAdvice 捕获 Filter 中的异常需自行 try-catch
Interceptor ❌ preHandle / postHandle 中的异常不会被捕获 afterCompletion 中的异常也不会被捕获
AOP ❌ AOP 切面中的异常不会被捕获 但 AOP @AfterThrowing 可以捕获目标方法的异常
Controller ✅ 可以被捕获 @RestControllerAdvice 主要捕获范围
Service ⚠️ 间接捕获 Service 抛出的异常传播到 Controller 后可被捕获

💡 如果异常在 Filter 或 Interceptor 中抛出,会直接传播到 Servlet 容器,由 ErrorController 处理。

# 常见使用场景

场景 说明
统一异常响应 所有异常返回统一格式的 JSON
业务异常处理 BusinessException 转换为友好提示
参数校验异常 MethodArgumentNotValidException 提取校验错误信息
权限异常处理 AccessDeniedException 返回 403
兜底异常处理 Exception.class 防止异常堆栈暴露给前端

# 跨域配置(CORS)

# 基本概念

CORS(Cross-Origin Resource Sharing,跨域资源共享)是浏览器的安全机制。当前端域名与后端 API 域名不一致时,浏览器会拦截响应。后端需要通过设置 CORS 响应头 来允许跨域访问。

# 三种实现方式

# 方式一:@CrossOrigin 注解(粒度最细)

@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "http://localhost:8080", maxAge = 3600)
public class ApiController {

    @GetMapping("/data")
    @CrossOrigin(origins = "http://localhost:8080") // 方法级别覆盖
    public Result<?> getData() {
        return Result.success();
    }
}
1
2
3
4
5
6
7
8
9
10
11

# 方式二:WebMvcConfigurer.addCorsMappings(全局配置)

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("http://localhost:8080")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600); // 预检请求缓存时间(秒)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 方式三:CorsFilter(推荐,最灵活)

@Configuration
public class CorsFilterConfig {

    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilterRegistration() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOrigins(Arrays.asList("http://localhost:8080"));
        config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        config.setAllowedHeaders(Arrays.asList("*"));
        config.setAllowCredentials(true);
        config.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);

        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new CorsFilter(source));
        bean.setOrder(0); // 优先级最高,确保在其他 Filter 之前处理 CORS
        return bean;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 三种方式对比

对比项 @CrossOrigin WebMvcConfigurer CorsFilter
粒度 类 / 方法级别 全局路径级别 全局路径级别
执行层级 Spring MVC 层 Spring MVC 层 Servlet 容器层
优先级 低 中 高(可设置 order)
灵活性 低 中 高
推荐场景 少量接口跨域 全局简单跨域 全局复杂跨域 / 有 Filter 拦截

⚠️ 如果项目中使用了 Spring Security,CORS 需在 Security Filter Chain 中配置,否则可能在 Security 拦截之前就被拒绝:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.cors(Customizer.withDefaults()) // 启用 CORS
            .csrf().disable()
            ...;
        return http.build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOrigins(Arrays.asList("http://localhost:8080"));
        config.setAllowedMethods(Arrays.asList("*"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# CORS 预检请求(Preflight)

浏览器对于非简单请求会先发送一个 OPTIONS 方法的预检请求:

// 简单请求(不触发预检)
GET /api/data
Content-Type: text/plain

// 非简单请求(触发预检)
POST /api/data
Content-Type: application/json
Authorization: Bearer xxx
1
2
3
4
5
6
7
8

预检请求流程:

浏览器 → OPTIONS /api/data(预检请求)
    │
    ▼
后端返回 CORS 响应头
    Access-Control-Allow-Origin: http://localhost:8080
    Access-Control-Allow-Methods: GET, POST, PUT, DELETE
    Access-Control-Allow-Headers: Content-Type, Authorization
    Access-Control-Max-Age: 3600
    │
    ▼
浏览器检查通过 → 发送真实请求
浏览器检查失败 → 拦截请求,不发送
1
2
3
4
5
6
7
8
9
10
11
12

💡 maxAge 设置预检缓存时间,缓存期内浏览器不会重复发送预检请求。

# 其他拦截 / 捕获机制

除了 Filter、Interceptor、AOP、@RestControllerAdvice,Spring 生态中还有以下机制可以拦截或捕获请求 / 异常:

# ResponseBodyAdvice(响应体增强)

在 Controller 方法返回后、响应体写入前,对返回值进行统一包装。

@RestControllerAdvice
public class GlobalResponseAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        // 对所有返回值生效
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        // 统一包装为 Result 格式
        if (body instanceof Result) {
            return body; // 已包装,直接返回
        }
        return Result.success(body);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
特性 说明
作用时机 Controller 返回后、JSON 序列化前
适用场景 统一响应体包装、敏感数据脱敏
与 @RestControllerAdvice 关系 可组合使用,异常处理 + 响应包装

# RequestBodyAdvice(请求体增强)

在请求体反序列化前后插入处理逻辑。

@RestControllerAdvice
public class GlobalRequestAdvice implements RequestBodyAdvice {

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType,
                            Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
                                           Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 请求体读取前处理(如解密)
        return inputMessage;
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
                                Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 请求体读取后处理(如参数校验)
        return body;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
                                  Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
特性 说明
作用时机 请求体反序列化前后
适用场景 请求参数解密、参数预处理

# ErrorController(兜底异常处理)

当异常未被任何 @ExceptionHandler 捕获时,Spring Boot 会转发到 /error 路径,由 ErrorController 处理。

@RestController
public class CustomErrorController implements ErrorController {

    @RequestMapping("/error")
    public Result<Void> handleError(HttpServletRequest request) {
        Integer status = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
        Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);

        if (status != null && status == 404) {
            return Result.fail(404, "资源不存在");
        }
        return Result.fail(500, "系统内部错误");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
特性 说明
触发条件 异常未被 @ExceptionHandler 捕获 / Filter 中抛出异常
执行层级 Servlet 容器层(最后防线)
适用场景 兜底异常处理、404 页面定制

💡 Filter / Interceptor 中抛出的异常无法被 @RestControllerAdvice 捕获,最终会由 ErrorController 处理。

# Spring Security Filter Chain(安全过滤器链)

Spring Security 通过一组 Security Filter 实现认证与授权,这些 Filter 在普通 Filter 之前执行。

HTTP 请求
    │
    ▼
SecurityFilterChain(Spring Security)
    │  ├── SecurityContextPersistenceFilter
    │  ├── UsernamePasswordAuthenticationFilter
    │  ├── JwtAuthenticationFilter(自定义)
    │  ├── ExceptionTranslationFilter
    │  └── FilterSecurityInterceptor
    │
    ▼
普通 Filter → DispatcherServlet → Interceptor → Controller
1
2
3
4
5
6
7
8
9
10
11
12
特性 说明
执行层级 在普通 Filter 之前
核心组件 SecurityFilterChain
适用场景 认证、授权、安全防护

💡 关于 Spring Security 的详细使用,可参考 安全框架 Spring Security 入门。

# Spring Cloud Gateway GlobalFilter(网关层过滤器)

在微服务架构中,Spring Cloud Gateway 提供 GlobalFilter 在网关层拦截所有请求。

@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (StringUtils.isBlank(token)) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -100; // 优先级越高,数字越小
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
特性 说明
执行层级 网关层(微服务之前)
适用场景 统一认证、限流、日志、灰度发布

# HandlerExceptionResolver(异常解析器)

Spring MVC 内部的异常解析接口,@ExceptionHandler 底层依赖 ExceptionHandlerExceptionResolver 实现。

@Component
public class CustomExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
                                         Object handler, Exception ex) {
        // 自定义异常解析逻辑
        response.setStatus(500);
        try {
            response.getWriter().write(JSON.toJSONString(Result.fail(500, ex.getMessage())));
        } catch (IOException e) {
            // ignore
        }
        return new ModelAndView(); // 返回空的 ModelAndView 表示已处理
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

💡 通常不需要直接实现 HandlerExceptionResolver,使用 @RestControllerAdvice + @ExceptionHandler 即可。

# 各机制总览

机制 层级 拦截范围 典型场景
Filter Servlet 容器 所有请求 编码、CORS、XSS
Interceptor Spring MVC Controller 请求 登录校验、权限控制
AOP Spring Bean Bean 方法 日志、事务、限流
@RestControllerAdvice Spring MVC Controller 异常 统一异常处理
ResponseBodyAdvice Spring MVC Controller 返回值 统一响应包装
RequestBodyAdvice Spring MVC 请求体 请求解密、预处理
ErrorController Servlet 容器 未捕获异常 兜底异常、404
Security Filter Chain Spring Security 所有请求 认证、授权
Gateway GlobalFilter 网关层 网关所有请求 统一认证、限流
HandlerExceptionResolver Spring MVC Controller 异常 自定义异常解析

# 各机制对比

对比项 Filter(过滤器) Interceptor(拦截器) AOP(切面) @RestControllerAdvice
技术规范 Servlet 规范 Spring MVC Spring AOP Spring MVC
运行层级 Servlet 容器 DispatcherServlet 内部 Spring Bean 代理层 DispatcherServlet 内部
拦截范围 所有请求(含静态资源) Controller 请求 Spring Bean 的方法 Controller 抛出的异常
实现机制 回调函数 Java 反射 动态代理(JDK / CGLIB) HandlerExceptionResolver
能否获取 Bean ❌ 不能直接获取(需通过 ApplicationContext) ✅ 可以 ✅ 可以 ✅ 可以
能否修改请求/响应对象 ✅ 可以(可包装为 HttpServletRequestWrapper) ✅ 可以 ❌ 不直接操作请求/响应 ✅ 可以(通过 @ResponseBody)
粒度 URL 级别 Controller 方法级别 任意 Bean 方法级别 异常类型级别
异常处理 需自行 try-catch afterCompletion 可获取异常 @AfterThrowing 可捕获异常 @ExceptionHandler 统一捕获
执行顺序 按 FilterRegistrationBean 的 order 按 addInterceptor 的注册顺序 按 @Order 注解或 @Priority 按 @Order + 异常类型匹配
典型场景 编码、CORS、XSS 过滤 登录校验、权限控制 日志、事务、缓存、限流 统一异常响应、参数校验

# 执行顺序详解

# 完整执行流程

当 Filter、Interceptor、AOP、@RestControllerAdvice 同时存在时,执行顺序如下:

请求到达
    │
    ▼
Filter.doFilter (前置)
    │
    ▼
DispatcherServlet
    │
    ▼
Interceptor.preHandle
    │
    ▼
AOP @Before / @Around (前置)
    │
    ▼
Controller 方法
    │
    ├─ 正常返回 ─→ AOP @After / @Around (后置) / @AfterReturning
    │                  │
    │                  ▼
    │              Interceptor.postHandle
    │                  │
    │                  ▼
    │              视图渲染
    │
    └─ 抛出异常 ─→ AOP @AfterThrowing / @After
                       │
                       ▼
                   @RestControllerAdvice @ExceptionHandler
                       │
                       ▼
                   Interceptor.afterCompletion (ex=null)
                       │
                       ▼
                   Filter.doFilter (后置)
                       │
                       ▼
                   响应返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# 简化记忆

请求:  Filter前 → Interceptor前 → AOP前 → Controller
正常:  Controller → AOP后 → Interceptor后 → Filter后
异常:  Controller → AOP异常 → @RestControllerAdvice → Interceptor.afterCompletion → Filter后
1
2
3

关键点:Filter 最先执行、最后结束;AOP 最靠近业务方法;@RestControllerAdvice 在异常时接管响应。三者是洋葱模型——外层先进入后退出。

# 多个 Filter / Interceptor 的顺序

# 多个 Filter

按 FilterRegistrationBean 的 order 值从小到大执行:

// order=1 的 Filter 先执行
registration.setOrder(1);
1
2
Filter1前 → Filter2前 → ... → Servlet → ... → Filter2后 → Filter1后
1

# 多个 Interceptor

按 addInterceptor 的注册顺序执行:

registry.addInterceptor(interceptorA); // 先注册,先执行
registry.addInterceptor(interceptorB); // 后注册,后执行
1
2
A.preHandle → B.preHandle → Controller → B.postHandle → A.postHandle → B.afterCompletion → A.afterCompletion
1

⚠️ 注意 postHandle 和 afterCompletion 的顺序是反过来的(类似栈结构)。

# 验证执行顺序的示例

定义一个 Filter、一个 Interceptor、一个 AOP 切面,在 Controller 方法上观察输出:

// Filter
System.out.println("1. Filter 前");
chain.doFilter(request, response);
System.out.println("6. Filter 后");

// Interceptor
System.out.println("2. Interceptor preHandle");
// ...
System.out.println("4. Interceptor postHandle");
// ...
System.out.println("5. Interceptor afterCompletion");

// AOP @Around
System.out.println("3. AOP 前");
Object result = joinPoint.proceed();
System.out.println("3.5 AOP 后");

// Controller
System.out.println("3.1 Controller 执行");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

输出结果:

1. Filter 前
2. Interceptor preHandle
3. AOP 前
3.1 Controller 执行
3.5 AOP 后
4. Interceptor postHandle
5. Interceptor afterCompletion
6. Filter 后
1
2
3
4
5
6
7
8

# 异常情况下的执行顺序

# Controller 抛出异常

Filter前 → Interceptor.preHandle → AOP前 → Controller(抛异常)
    → AOP @AfterThrowing / @After
    → @RestControllerAdvice @ExceptionHandler(捕获异常,生成响应)
    → Interceptor.afterCompletion(ex=null,异常已被处理)
    → Filter后
1
2
3
4
5
组件 异常时是否执行
Filter 后置逻辑 ✅ 执行
Interceptor postHandle ❌ 不执行
Interceptor afterCompletion ✅ 执行(ex 为 null,异常已被 @RestControllerAdvice 处理)
AOP @AfterReturning ❌ 不执行
AOP @AfterThrowing ✅ 执行
AOP @After ✅ 执行
@RestControllerAdvice ✅ 执行(捕获 Controller 异常)

💡 这就是为什么 afterCompletion 适合做资源清理——无论是否异常都会执行。

# preHandle 返回 false

Filter1前 → Filter2前 → Interceptor1.preHandle(返回true)
    → Interceptor2.preHandle(返回false)  ← 中断
    → Interceptor1.afterCompletion  ← 已执行的拦截器仍会回调
    → Filter2后 → Filter1后
1
2
3
4

Controller、AOP、postHandle 都不会执行。

# 参数解析异常

参数解析发生在 AOP 之前,

所以 MethodArgumentNotValidException、HttpMessageNotReadableException(请求体格式错误)、MissingServletRequestParameterException(缺少必填参数)这些都打不到 AOP 日志里。

请求 → Filter → DispatcherServlet → 参数解析(@RequestBody 反序列化 / @Valid 校验)
    → 失败直接抛异常,Controller 方法根本没被调用
    → AOP 包裹的是 Controller 方法,方法没执行,AOP 不触发
    → 异常直接到 @RestControllerAdvice
1
2
3
4

# 如何选择?

# 决策流程

需要拦截所有请求(含静态资源)?
    → 是 → 用 Filter
    → 否 → 需要操作 HttpServletRequest / HttpServletResponse?
              → 是 → 用 Interceptor
              → 否 → 需要拦截非 Controller 的 Bean 方法?
                        → 是 → 用 AOP
                        → 否 → 需要统一处理 Controller 异常?
                                  → 是 → 用 @RestControllerAdvice
                                  → 否 → 需要统一包装响应体?
                                            → 是 → 用 ResponseBodyAdvice
                                            → 否 → 用 Interceptor
1
2
3
4
5
6
7
8
9
10
11

# 场景速查

需求 推荐方案
统一字符编码 Filter
CORS 跨域 Filter(或 Spring CORS 配置)
XSS / SQL 注入过滤 Filter
登录状态校验 Interceptor
接口权限控制 Interceptor
接口耗时统计 Interceptor 或 AOP
Service 方法日志 AOP
自定义注解实现功能(限流等) AOP
事务管理 AOP(@Transactional)
请求体缓存(可重复读取) Filter
统一异常处理 @RestControllerAdvice
统一响应体包装 ResponseBodyAdvice
请求参数解密 RequestBodyAdvice
兜底异常 / 404 处理 ErrorController
认证授权(Spring Security) Security Filter Chain
网关层统一拦截 Gateway GlobalFilter

# 常见问题

# 1. Filter 中无法注入 Bean?

原因:Filter 由 Servlet 容器管理,早于 Spring 容器初始化。

解决方法:

// 方式一:使用 FilterRegistrationBean 注册(推荐)
@Bean
public FilterRegistrationBean<MyFilter> myFilterRegistration() {
    FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>();
    registration.setFilter(myFilter); // myFilter 由 Spring 注入,可以使用 @Autowired
    registration.addUrlPatterns("/*");
    return registration;
}

// 方式二:通过 ApplicationContextAware 获取
1
2
3
4
5
6
7
8
9
10

# 2. Interceptor 中无法注入 Bean?

解决方法:确保 Interceptor 通过 @Component 注册为 Bean,并在 addInterceptors 中注入:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private MyInterceptor myInterceptor; // 注入即可使用 @Autowired

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor).addPathPatterns("/**");
    }
}
1
2
3
4
5
6
7
8
9
10
11

# 3. AOP 不生效?

常见原因:

原因 说明 解决方法
目标类未由 Spring 管理 AOP 只能代理 Spring Bean 添加 @Component 等注解
内部方法调用 this.method() 不走代理 使用 AopContext.currentProxy() 或注入自身
目标方法是 final 或 static 无法被代理覆盖 去除 final / static 修饰
未添加 @EnableAspectJAutoProxy Spring Boot 默认已开启,纯 Spring 项目需手动添加 添加注解

内部调用问题示例:

@Service
public class UserService {

    public void methodA() {
        // ❌ 直接调用,不经过代理,AOP 不生效
        this.methodB();
    }

    @LogAnnotation // 期望被 AOP 拦截
    public void methodB() { }

    // ✅ 解决方法:通过代理对象调用
    public void methodA_fixed() {
        ((UserService) AopContext.currentProxy()).methodB();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 4. Filter 和 Interceptor 都能做登录校验,选哪个?

推荐 Interceptor,原因:

  • 可以方便地注入 Service 进行 Token 校验
  • 只拦截 Controller 请求,不影响静态资源
  • preHandle 返回 false 即可中断请求,语义清晰

如果需要在请求最外层统一处理(如网关层),则用 Filter。

# 总结

要点 Filter Interceptor AOP @RestControllerAdvice
层级 Servlet 容器 Spring MVC Spring Bean 代理 Spring MVC
触发时机 请求最外层 Controller 前后 Bean 方法前后 Controller 抛出异常时
执行顺序 最先执行,最后结束 中间层 最内层,最靠近业务 异常时接管响应
核心接口 Filter HandlerInterceptor @Aspect + @Pointcut @ExceptionHandler
选择原则 需要拦截所有请求 / 操作请求响应对象 需要拦截 Controller / 可注入 Bean 需要拦截任意 Bean 方法 / 细粒度控制 需要统一处理 Controller 异常

记忆口诀:Filter 在外管全局,Interceptor 居中管 Controller,AOP 在内管方法,@RestControllerAdvice 兜底管异常。洋葱层层包裹,外层先进后出,异常由内向外传递,@RestControllerAdvice 截断异常链。

上次更新: 2026/8/14 18:06:06
日志记录相关
缓存设计分析

← 日志记录相关 缓存设计分析→

Theme by Vdoing | Copyright © 2023-2026 沉梦听雨 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式