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

Spring Boot 全局异常处理问题分析与解决方案

文章目录

  • Spring Boot 全局异常处理问题分析与解决方案
    • 问题背景
    • 问题现象
    • 问题分析
    • 解决方案
      • 1. 在过滤器中手动处理异常
        • 优点
        • 缺点
        • 示例代码
      • 2. 使用 `ErrorController` 或 `ErrorAttributes`
        • **优点**
        • **缺点**
        • **示例代码**
      • 3. 使用 AOP(面向切面编程)
        • **优点**
        • **缺点**
        • **示例代码**
      • 4. 使用自定义的异常处理框架
        • **优点**
        • **缺点**
        • **示例代码**

Spring Boot 全局异常处理问题分析与解决方案

问题背景

在 Spring Boot 项目中,全局异常处理器(GlobalExceptionHandler)通常用于捕获和处理控制器中抛出的异常,并返回统一的响应格式。然而,当异常发生在过滤器(如 LoginCheckFilter)中时,全局异常处理器可能无法捕获这些异常,导致返回的响应格式不符合预期。

问题现象

  1. 用户登录失败

    • 在控制器中抛出的 UserException 被全局异常处理器捕获,并返回自定义的 Result 格式。
    • 日志示例
      2025-09-03 17:50:54.948 ERROR 52880 --- [.0-8080-exec-10] c.f.g.exception.GlobalExceptionHandler   : 用户错误信息:登录失败,用户名或密码错误
      
      在这里插入图片描述
  2. JWT 解析失败

    • 在过滤器中抛出的 UserException 未被全局异常处理器捕获,导致 Spring Boot 的默认错误处理机制接管,返回标准的错误响应。
    • 日志示例
      2025-09-03 17:53:58.293 ERROR 52880 --- [0.0-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
      com.financialfinshieldguard.gold.exception.UserException: jwt令牌无法正确解析
      

在这里插入图片描述

问题分析

  1. 全局异常处理器的限制

    • @RestControllerAdvice@ControllerAdvice 默认只捕获控制器方法中抛出的异常。对于过滤器中抛出的异常,这些注解不会生效。
    • 关键点:过滤器中的异常不会自动被全局异常处理器捕获。
  2. 过滤器中的异常处理

    • 在 Spring Boot 中,过滤器中的异常不会自动被全局异常处理器捕获。需要手动处理这些异常,并将其转换为自定义的响应格式。

解决方案

1. 在过滤器中手动处理异常

通过在过滤器中捕获异常,并手动调用全局异常处理器的方法来生成自定义的响应。

优点
  • 灵活性高:可以在过滤器中对不同类型的异常进行精细控制。
  • 一致性:确保过滤器中的异常也能返回与控制器中一致的响应格式。
缺点
  • 代码重复:需要在每个过滤器中手动处理异常,可能导致代码重复。
  • 维护成本高:如果全局异常处理逻辑发生变化,需要在多个地方更新代码。
示例代码
public class LoginCheckFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;HttpServletResponse httpResponse = (HttpServletResponse) response;try {// 假设这里调用了 JwtUtil.parse 方法chain.doFilter(request, response);} catch (UserException e) {// 手动调用全局异常处理器的方法GlobalExceptionHandler globalExceptionHandler = new GlobalExceptionHandler();Result<?> result = globalExceptionHandler.handleUserException(e);// 设置响应内容类型和状态码httpResponse.setContentType("application/json;charset=UTF-8");httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);// 写入自定义的响应内容httpResponse.getWriter().write(result.toString());}}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 初始化逻辑}@Overridepublic void destroy() {// 销毁逻辑}
}

2. 使用 ErrorControllerErrorAttributes

通过实现 ErrorController 或自定义 ErrorAttributes,可以捕获所有类型的异常,包括过滤器中抛出的异常,并返回自定义的响应格式。

优点
  • 全局统一:可以统一处理所有异常,包括过滤器和控制器中的异常。
  • 减少重复代码:避免在多个地方重复处理异常逻辑。
  • 易于维护:全局异常处理逻辑集中在一个地方,便于维护和更新。
缺点
  • 复杂性增加:实现 ErrorControllerErrorAttributes 可能会增加代码的复杂性。
  • 性能影响:全局异常处理可能会引入额外的性能开销,尤其是在高并发场景下。
示例代码
@RestController
public class CustomErrorController implements ErrorController {@RequestMapping("/error")public ResponseEntity<Result<?>> handleError(HttpServletRequest request) {Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);if (status != null) {Integer statusCode = Integer.valueOf(status.toString());if (statusCode == HttpStatus.BAD_REQUEST.value()) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Result.UserError(400, "jwt令牌无法正确解析"));}}return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.ServerError("未知错误"));}@Overridepublic String getErrorPath() {return "/error";}
}

3. 使用 AOP(面向切面编程)

通过 AOP 拦截器捕获过滤器和控制器中的异常,并统一处理。

优点
  • 解耦:将异常处理逻辑与业务逻辑解耦,提高代码的可维护性和可读性。
  • 灵活性:可以在不同的切点上应用不同的异常处理逻辑。
缺点
  • 学习曲线:AOP 的概念和实现可能对一些开发人员来说有一定的学习曲线。
  • 调试困难:AOP 代码的调试可能比普通代码更复杂。
示例代码
@Aspect
@Component
public class GlobalExceptionAspect {@Around("execution(* com.financialfinshieldguard.gold.controller.*.*(..)) || execution(* com.financialfinshieldguard.gold.filter.*.*(..))")public Object handleException(ProceedingJoinPoint joinPoint) throws Throwable {try {return joinPoint.proceed();} catch (UserException e) {Result<?> result = Result.UserError(e.getCode(), e.getMessage());return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);} catch (Throwable e) {Result<?> result = Result.ServerError(e.getMessage());return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);}}
}

4. 使用自定义的异常处理框架

一些企业会开发自己的异常处理框架,结合上述多种技术,提供统一的异常处理机制。

优点
  • 定制化:可以根据企业的具体需求定制异常处理逻辑。
  • 集成性:可以与现有的技术栈无缝集成,提供一致的异常处理体验。
缺点
  • 开发成本:开发和维护自定义框架需要额外的资源和时间。
  • 复杂性:自定义框架可能会增加系统的复杂性,特别是对于新加入的开发人员。
示例代码
// 自定义异常处理框架的实现代码示例
// 这里假设有一个自定义的异常处理框架类 CustomExceptionFramework@Component
public class CustomExceptionFramework {@Autowiredprivate GlobalExceptionHandler globalExceptionHandler;public ResponseEntity<Result<?>> handleException(Throwable throwable) {if (throwable instanceof UserException) {UserException userException = (UserException) throwable;return globalExceptionHandler.handleUserException(userException);} else {return globalExceptionHandler.handleException(throwable);}}
}# 思:我得学一下AOP!!!
http://www.xdnf.cn/news/19789.html

相关文章:

  • PastePal for Mac 剪贴板历史记录管理器
  • prometheus+grafana搭建
  • 7.4Element Plus 分页与表格组件
  • js设计模式-装饰器模式
  • CI(持续集成)、CD(持续交付/部署)、CT(持续测试)、CICD、CICT
  • Few-Shot Prompting 实战:用5个例子让GPT-4学会复杂任务
  • (二)文件管理-基础命令-cd命令的使用
  • 中州养老:资源添加功能
  • gitlab推送失败,内存不足的处理
  • 深入浅出Spring IoC与DI:设计思想、实现方式与反射技术详解
  • Excel 电影名匹配图片路径教程:自动查找并写入系统全路径
  • PostgreSQL 中唯一索引的工作原理
  • 分布式AI算力系统番外篇-----超体的现实《星核》
  • Vue基础知识-重要的内置关系:vc实例.__proto__.__proto__ === Vue.prototype
  • 股指期货可以通过移仓长线持有吗?
  • AppInventor2 如何自定义包名?
  • 华为云云原生架构赋能:大腾智能加速业务创新步伐
  • 【深度学习新浪潮】视觉大模型在预训练方面有哪些关键进展?
  • 鸿蒙UI开发实战:解决布局错乱与响应异常
  • 企业实用——MySQL的备份详解
  • 基于机器学习的Backtrader波动性预测与管理
  • Kubernetes ConfigMap 更新完整指南:原理、方法与最佳实践
  • PyTorch实战——ResNet与DenseNet详解
  • Huggingface终于没忍住,OpenCSG坚持开源开放
  • flume拓扑结构详解:从简单串联到复杂聚合的完整指南
  • Linux 的信号 和 Qt 的信号
  • IO_HW_9_3
  • MySQL数据库恢复步骤(基于全量备份和binlog)
  • 揭秘ArrowJava核心:IndexSorter高效排序设计
  • Cookie、Session、登录