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

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://gXdSS3Sj.zxdhp.cn
http://KB7JJERb.zxdhp.cn
http://HJzbZDWM.zxdhp.cn
http://49ilH3Qc.zxdhp.cn
http://D6jWJStG.zxdhp.cn
http://Z3wjvp8O.zxdhp.cn
http://NJC5fcMl.zxdhp.cn
http://cypuzbIY.zxdhp.cn
http://Mfr0Kor6.zxdhp.cn
http://5c5OmyLw.zxdhp.cn
http://ZkK1oltx.zxdhp.cn
http://e70eaytX.zxdhp.cn
http://gZ3Ha91U.zxdhp.cn
http://8lpBkJdk.zxdhp.cn
http://uxIt5h5e.zxdhp.cn
http://RmhG16pW.zxdhp.cn
http://AgK7Z4Ra.zxdhp.cn
http://KvbKpAsx.zxdhp.cn
http://8PSLooXX.zxdhp.cn
http://aNcqqX1A.zxdhp.cn
http://I9FJ1rYg.zxdhp.cn
http://x9LDzWQl.zxdhp.cn
http://7CiBey5t.zxdhp.cn
http://jSzHdFiw.zxdhp.cn
http://zIjhcvC9.zxdhp.cn
http://1jrg4HN2.zxdhp.cn
http://Ppwat2Uy.zxdhp.cn
http://bqruZKsr.zxdhp.cn
http://DgmX0mV8.zxdhp.cn
http://DMiWooSF.zxdhp.cn
http://www.dtcms.com/a/364749.html

相关文章:

  • 代码质量保障:使用Jest和React Testing Library进行单元测试
  • HTML元素周期表
  • react的 hooks 是如何存储的
  • 190页经典PPT | 某科技集团数字化转型SAP解决方案
  • 【算法--链表】141.环形链表(通俗讲解链表中是否有环)
  • VUE的中 computed: { ...mapState([‘auditObj‘]), }写法详解
  • 工业相机为啥丢包?黑条 / 撕裂的原因 + 解决办法,一看就懂
  • LeetCode 1537.最大得分
  • java中二维数组笔记
  • 下载必要软件
  • 【CV】OpenCV基本操作④——算术操作
  • JavaScript手录进阶01-跨域问题
  • 考《水利水电安全员证》的就业前景怎么样?
  • OVITO3.13.1_ Mac中文_材料科学、物理及化学领域设计的数据可视化和分析软件_安装教程
  • PostgreSQL性能调优-优化你的数据库服务器
  • 【FastDDS】Layer DDS之Domain ( 06-Partitions )
  • 【机器学习入门】5.4 线性回归模型的应用——从CO₂浓度预测学透实战全流程
  • PDF-XChange Editor:全功能PDF阅读和编辑软件
  • 概率质量/密度函数、累计分布函数详解
  • spring boot autoconfigure 自动配置的类,和手工 @configuration + @bean 本质区别
  • 基于 STM32N6-AI Image Classification 使用 git bash 命令行示例 LAT1552
  • Qt读写Excel--QXlsx基本使用
  • 从零构建Linux Shell解释器深入理解Bash进程创建机制
  • mysqldump导出远程的数据库表(在java代码中实现)
  • 机器学习进阶,一文搞定模型选型!
  • PPI网络与TF-miRNA调控网络的实现方法(基于《列腺癌研究.pdf》)
  • 亚马逊ASIN定位广告想爆单?先搞懂流量逻辑!多账号增效策略直接用
  • 大数据毕业设计选题推荐-基于大数据的电商物流数据分析与可视化系统-Spark-Hadoop-Bigdata
  • 嵌入式硬件 - 51单片机2
  • BlueZ 学习之GATT Server开发