当前位置: 首页 > news >正文

SpringBoot 统一功能处理:拦截器、统一返回与异常处理

在 SpringBoot 开发中,统一功能处理是提升代码复用性、降低维护成本的关键。本文将围绕拦截器、统一数据返回格式、统一异常处理三大核心功能,结合实战案例与源码解析,带你掌握 SpringBoot 统一功能处理的实现方案。

一、拦截器:请求的 “守门人”

在传统开发中,若需对多个接口做登录校验,需在每个接口重复编写 Session 判断逻辑,冗余且难维护。拦截器作为 Spring 框架核心功能,可统一拦截请求,在目标方法执行前后嵌入自定义逻辑,完美解决此类问题。

1.1 拦截器核心概念

拦截器是 Spring MVC 提供的请求拦截机制,能在请求到达 Controller 前、Controller 执行后、视图渲染完成后执行自定义逻辑,主要用于:

  • 登录状态校验
  • 接口访问权限控制
  • 请求参数预处理 / 日志记录

1.2 拦截器使用步骤

拦截器的实现需两步:定义拦截器 + 注册配置拦截器

步骤 1:定义拦截器(实现 HandlerInterceptor 接口)

需重写三个核心方法,分别对应不同执行时机:

@Slf4j
@Component // 注入Spring容器
public class LoginInterceptor implements HandlerInterceptor {/*** 目标方法执行前执行(核心:请求拦截逻辑)* @return true:放行;false:拦截(后续逻辑不执行)*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("LoginInterceptor:目标方法执行前执行");// 登录校验逻辑:从Session获取用户信息HttpSession session = request.getSession(false); // 不自动创建Sessionif (session != null && session.getAttribute("SESSION_USER_KEY") != null) {return true; // 已登录,放行}// 未登录,返回401(Unauthorized)response.setStatus(401);return false;}/** 目标方法执行后执行(视图渲染前,后端开发较少用) */@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("LoginInterceptor:目标方法执行后执行");}/** 视图渲染完毕后执行(最后执行,可做资源清理) */@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("LoginInterceptor:视图渲染完毕后执行");}
}
步骤 2:注册配置拦截器(实现 WebMvcConfigurer)

通过 addPathPatterns 指定拦截路径,excludePathPatterns 指定排除路径(如登录接口、静态资源):

@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**") // 拦截所有请求.excludePathPatterns( // 排除无需拦截的路径"/user/login",       // 登录接口"/**/*.js", "/**/*.css", // 静态资源(JS/CSS)"/**/*.png", "/**/*.html"  // 静态资源(图片/HTML));}
}
拦截路径规则

不同路径表达式对应不同拦截范围,具体如下:

拦截路径含义示例
/*一级路径匹配 /user、/book,不匹配 /user/login
/**任意级路径匹配 /user、/user/login、/book/add
/book/*/book 下一级路径匹配 /book/add,不匹配 /book/add/1
/book/**/book 下任意级路径匹配 /book、/book/add、/book/add/2

1.3 拦截器执行流程

结合 Spring MVC 核心组件 DispatcherServlet,拦截器的执行流程如下:

  1. 请求先进入 DispatcherServlet,由其调用 getHandler 获取请求对应的拦截器链;
  2. 执行拦截器链的 preHandle 方法:若返回 true,继续执行下一个拦截器;若返回 false,中断请求;
  3. 所有 preHandle 放行后,执行 Controller 目标方法;
  4. 目标方法执行完成后,反向执行拦截器链的 postHandle 方法;
  5. 视图渲染完成(后端开发几乎不涉及),反向执行 afterCompletion 方法;
  6. 最终通过 DispatcherServlet 向客户端返回响应。

1.4 源码解析:DispatcherServlet 核心作用

DispatcherServlet 是 Spring MVC 的 “中央调度器”,所有请求都需经过它处理。其核心逻辑在 doDispatch 方法中,关键步骤包括:

  1. 获取执行链:通过 getHandler 找到请求对应的拦截器链(HandlerExecutionChain);
  2. 获取适配器:通过 getHandlerAdapter 找到适配 Controller 的 HandlerAdapter(适配模式应用);
  3. 执行拦截器 preHandle:调用 mappedHandler.applyPreHandle,遍历执行所有拦截器的 preHandle
  4. 执行目标方法:通过 handlerAdapter.handle 调用 Controller 方法;
  5. 执行拦截器 postHandle 与 afterCompletion:目标方法执行后,反向执行后续拦截器逻辑。

二、统一数据返回格式:前后端协作的 “语言规范”

前后端分离项目中,若每个接口返回格式不一致(如有的返回实体类、有的返回 String、有的返回 Boolean),会增加前端解析成本。统一数据返回格式可解决此问题,让所有接口返回结构一致。

2.1 实现方案:@ControllerAdvice + ResponseBodyAdvice

通过 @ControllerAdvice(控制器通知类)结合 ResponseBodyAdvice(响应体增强接口),可在响应返回前统一封装数据。

步骤 1:定义统一返回实体类

先定义一个通用的返回结构 Result,包含状态码、错误信息、数据体:

@Data
public class Result<T> {private String status;       // 状态:SUCCESS/FAILprivate String errorMessage; // 错误信息(失败时非空)private T data;              // 数据体(成功时非空)// 成功响应静态方法public static <T> Result<T> success(T data) {Result<T> result = new Result<>();result.setStatus("SUCCESS");result.setData(data);return result;}// 失败响应静态方法public static <T> Result<T> fail(String errorMessage) {Result<T> result = new Result<>();result.setStatus("FAIL");result.setErrorMessage(errorMessage);result.setData(null);return result;}
}
步骤 2:实现 ResponseBodyAdvice 统一封装

通过 ResponseBodyAdvice 的 beforeBodyWrite 方法,在响应返回前对数据进行封装:

@Slf4j
@ControllerAdvice // 全局控制器通知
public class ResponseAdvice implements ResponseBodyAdvice<Object> {private static final ObjectMapper MAPPER = new ObjectMapper(); // Jackson序列化工具/*** 是否对响应进行处理(true:处理;false:跳过)*/@Overridepublic boolean supports(MethodParameter returnType, Class<?> converterType) {return true; // 对所有响应生效}/*** 响应返回前执行:统一封装为Result格式*/@SneakyThrows // 简化异常处理@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<?> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {// 1. 若已为Result类型,直接返回(避免重复封装)if (body instanceof Result) {return body;}// 2. 若为String类型,需手动序列化(解决StringHttpMessageConverter类型转换问题)if (body instanceof String) {return MAPPER.writeValueAsString(Result.success(body));}// 3. 其他类型,统一封装为成功响应return Result.success(body);}
}

2.2 关键问题:String 类型响应的特殊处理

若不处理 String 类型,会出现 ClassCastException: Result cannot be cast to String 异常。原因是:

  • Spring MVC 默认注册了 StringHttpMessageConverter(处理 String 响应)和 MappingJackson2HttpMessageConverter(处理 JSON 响应);
  • 当返回 String 时,Spring 优先使用 StringHttpMessageConverter,但该转换器无法将 Result 类型(封装后的数据)转为 String;
  • 解决方案:手动使用 Jackson 将 Result 序列化为 String,避免类型转换异常。

2.3 统一返回的优势

  1. 降低沟通成本:前后端无需针对每个接口协商返回格式;
  2. 简化前端解析:前端可通过固定字段(如 status 判断成功 / 失败,data 获取数据)处理所有接口;
  3. 便于维护:若需修改返回结构,只需调整 Result 类和 ResponseAdvice,无需修改所有接口。

三、统一异常处理:优雅应对程序 “意外”

程序运行中难免出现异常(如空指针、除零异常、自定义业务异常),若未统一处理,会返回默认的 500 错误页面或杂乱的异常信息。统一异常处理可捕获所有异常,返回结构化的错误响应。

3.1 实现方案:@ControllerAdvice + @ExceptionHandler

@ExceptionHandler 是异常处理器注解,结合 @ControllerAdvice 可全局捕获异常,并返回统一格式的错误响应。

步骤 1:全局异常处理器实现
@Slf4j
@ControllerAdvice // 全局生效
@ResponseBody     // 确保返回JSON格式
public class GlobalExceptionHandler {/*** 捕获所有Exception(兜底处理)*/@ExceptionHandler(Exception.class)public Result<Void> handleException(Exception e) {log.error("全局异常捕获:", e); // 记录异常日志return Result.fail("系统异常:" + e.getMessage());}/*** 捕获NullPointerException(特定异常优先处理)*/@ExceptionHandler(NullPointerException.class)public Result<Void> handleNullPointerException(NullPointerException e) {log.error("空指针异常捕获:", e);return Result.fail("空指针异常:" + e.getMessage());}/*** 捕获ArithmeticException(如除零异常)*/@ExceptionHandler(ArithmeticException.class)public Result<Void> handleArithmeticException(ArithmeticException e) {log.error("算术异常捕获:", e);return Result.fail("算术异常:" + e.getMessage());}// 可扩展:添加自定义业务异常处理(如UserNotLoginException)
}
步骤 2:异常匹配规则

当程序抛出异常时,Spring 会按 “最具体异常优先” 的原则匹配处理器:

  • 例如抛出 NullPointerException,会优先匹配 handleNullPointerException,而非兜底的 handleException
  • 匹配逻辑由 ExceptionHandlerMethodResolver 实现,通过异常类型的继承深度排序(子类异常处理器优先)。

3.2 实战效果

  • 访问 /test/t2(执行 10/0),抛出 ArithmeticException,返回:

    json

    {"status": "FAIL","errorMessage": "算术异常:/ by zero","data": null
    }
    
  • 访问 /test/t3(执行 null.length()),抛出 NullPointerException,返回:

    json

    {"status": "FAIL","errorMessage": "空指针异常:Cannot invoke \"String.length()\" because \"a\" is null","data": null
    }
    

四、@ControllerAdvice 源码解析:全局能力的 “基石”

@ControllerAdvice 是实现统一数据返回和统一异常处理的核心注解,其本质是一个 @Component(可被 Spring 扫描注入),通过属性(如 basePackages)指定生效范围。

4.1 核心源码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 本质是Spring组件
public @interface ControllerAdvice {// 指定生效的包(默认所有包)@AliasFor("basePackages")String[] value() default {};@AliasFor("value")String[] basePackages() default {};// 指定生效的类(如仅对UserController生效)Class<?>[] assignableTypes() default {};// 指定生效的注解(如仅对@RestController生效)Class<? extends Annotation>[] annotations() default {};
}

4.2 生效原理

  1. 统一数据返回DispatcherServlet 初始化时,通过 initHandlerAdapters 加载 RequestMappingHandlerAdapter;该适配器会扫描所有 @ControllerAdvice 标注的类,若实现 ResponseBodyAdvice,则加入 requestResponseBodyAdvice 列表;当响应返回时,调用 ResponseBodyAdvice 的 beforeBodyWrite 方法进行封装。
  2. 统一异常处理DispatcherServlet 初始化时,通过 initHandlerExceptionResolvers 加载 ExceptionHandlerExceptionResolver;该解析器会扫描所有 @ControllerAdvice 标注的类,提取 @ExceptionHandler 方法存入 exceptionHandlerAdviceCache;当抛出异常时,通过 ExceptionHandlerMethodResolver 匹配对应的处理器方法。

五、总结与实战建议

5.1 核心知识点总结

功能实现方案核心组件 / 注解作用
拦截器实现 HandlerInterceptor + 配置 WebMvcConfigurerHandlerInterceptorWebMvcConfigurer统一请求拦截(登录校验、权限控制)
统一数据返回@ControllerAdvice + ResponseBodyAdvice@ControllerAdviceResponseBodyAdvice所有接口返回格式统一为 Result
统一异常处理@ControllerAdvice + @ExceptionHandler@ControllerAdvice@ExceptionHandler全局捕获异常,返回结构化错误响应

5.2 实战建议

  1. 拦截器排除静态资源:前端的 JS、CSS、图片等静态资源无需拦截,避免影响页面加载;
  2. 自定义业务异常:在实际项目中,建议定义业务异常(如 UserNotLoginExceptionPermissionDeniedException),结合 @ExceptionHandler 实现更精细的异常处理;
  3. 日志记录:在异常处理器中务必记录异常栈信息(如 log.error("异常信息:", e)),便于问题排查;
  4. 接口评审:统一功能处理需在项目初期确定(如返回格式、异常码规范),避免后期大规模修改。

通过本文的方案,可大幅提升 SpringBoot 项目的可维护性和前后端协作效率,是企业级开发的必备技能。

http://www.dtcms.com/a/406352.html

相关文章:

  • MySQL 8.0 核心转储优化指南
  • MySQL 学习笔记 (Part.2)
  • 什么是数据治理?有哪些好用的数据治理平台?
  • 【Dubbo】Rpc与HTTP的区别、Dubbo调用过程
  • 网站需要怎么做的吗wordpress nova
  • php 做的应用网站wordpress 模板之家
  • PDFParser 的pickle.loads 寻找链(源码)wmctf2025-pdf2text
  • 如何在业务端进行正确的 reCAPTCHA 验证时序设计
  • 小九源码-springboot048-基于spring boot心理健康服务系统
  • 网站建设收费标准网络营销技术
  • 信息安全基础知识:04网络安全体系
  • 高古楼网站 做窗子pc 手机站网站制作
  • 郑州企业网站快速优化多少钱搜索词和关键词
  • 视频内容审核API选型指南:10大主流接口功能对比
  • shell 编程(1)——vim安装与使用
  • 【后端】.NET Core API框架搭建(11) --对象映射
  • Django+FastAPI+Vue微服务架构指南
  • 今日策略:年化436%,回撤7%,夏普比5.28, deap因子挖掘重构,附python代码
  • 【openrouter.ai】截止2025年9月底 openrouter可用模型清单列表(包含free免费的模型)
  • (13)ASP.NET Core2.2 中的选项模式(Options)
  • 做公司网站需要准备什么苏州网站建设企业
  • giflib5.2.2 在Qt与VS C++中实现Gif缩放示例
  • 题解:AT_abc401_c [ABC401C] K-bonacci
  • 【OpenFeign】在 RuoYi 框架中优雅使用 OpenFeign:从原理到实践与踩坑总结
  • VMware虚拟机安装ubuntu
  • 因果推断落地:从CausalML到EconML,详解Uplift建模核心库
  • 备案个人网站做淘宝客wordpress能做企业站吗
  • 天玑与骁龙芯片:性能对决与选择指南
  • 【RustPython】 RustPython Cargo.toml 配置文件详解
  • 献县网站做映射后 内网无法通过域名访问网站