SpringBoot之统一异常处理

SpringBoot之统一异常处理

目录

统一异常处理

代码示例 

运行结果 

问题

结合源码了解问题源头 

优点


统一异常处理

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的,
@ControllerAdvice 表⽰控制器通知类, @ExceptionHandler 是异常处理器,两个结合表
⽰当出现异常的时候执⾏某个通知,也就是执⾏某个方法事件。
代码示例 

ExceptionAdvice 

接⼝返回为数据时, 需要加 @ResponseBody 注解!!!

import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import springbook.model.Result; @Slf4j @ResponseBody @ControllerAdvice public class ExceptionAdvice { @ExceptionHandler public Result handlerException(Exception e){ log.error("发生异常, e: {}", e); return Result.fail("内部错误"); } @ExceptionHandler public Result handlerException(NullPointerException e){ log.error("发生异常, e:", e); return Result.fail("发生空指针异常"); } @ExceptionHandler public Result handlerException(ArithmeticException e){ log.error("发生异常, e:", e); return Result.fail("发生算术异常"); } }

 Result

import lombok.Data; import springbook.enums.ResultStatus; @Data public class Result<T> { private ResultStatus code; private String errMsg; //错误信息, 如果业务成功, errMsg为空 private T data; public static <T> Result success(T data){ Result result = new Result<>(); result.setCode(ResultStatus.SUCCESS); result.setData(data); return result; } public static Result fail(String msg){ Result result = new Result<>(); result.setCode(ResultStatus.FAIl); result.setErrMsg(msg); return result; } public static Result fail(ResultStatus resultStatus, String msg){ Result result = new Result<>(); result.setCode(resultStatus); result.setErrMsg(msg); return result; } }

TestController

import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/test") @RestController public class TestController { @RequestMapping("/t1") public String t1(){ int result = 10/0; return "string"; } @RequestMapping("/t2") public Integer t2(){ String str = null; System.out.println(str.length()); return 1; } @RequestMapping("/t3") public Boolean t3(){ Integer[] integers = new Integer[]{1,2,3,4}; System.out.println(integers[5]); return true; } }
运行结果 

t1的运行结果:

t2的运行结果:

t3的运行结果:

问题

我们知道,ArithmeticException和NullPointerException都属于RuntimeException,同时RuntimeException又属于Exception。

那么为什么同时有处理ArithmeticException和Exception的方法时,针对ArithmeticException异常,会调用处理ArithmeticException异常的方法而不是调用处理Exception异常的方法?

那么为什么同时有处理NullPointerException和Exception的方法时,针对NullPointerException异常,会调用处理NullPointerException异常的方法而不是调用处理Exception异常的方法?

结合源码了解问题源头 

下面通过Debug的方式,结合源码来剖析为什么会出现上面的问题。

以上面代码的访问test/t1路径下方法导致ArithmeticException异常为例:

经过Debug,我了解到,处理异常会走到下图所示的ExceptionHandlerMethodResolver类里面的getMapperMethod()方法:

在刚进入该方法的时候,经过第一个黄色框内代码的处理后,会把当前具有处理异常的方法结合当前发生的异常进行匹配,最终匹配到两个合适类型的异常,分别是Exception异常和ArithmeticException异常:

但是在经过第二个黄色框内的代码处理后,会对刚刚匹配到的异常进行排序,根据异常的深度进行排序,并返回深度最浅的异常(即返回最合适的异常)。

经过第二个黄色框内的代码处理后,matches里面的内容顺序发生变动:

此时,我们就知道了为什么同时有处理ArithmeticException和Exception的方法时,针对ArithmeticException异常,会调用处理ArithmeticException异常的方法而不是调用处理Exception异常的方法;以及为什么同时有处理NullPointerException和Exception的方法时,针对NullPointerException异常,会调用处理NullPointerException异常的方法而不是调用处理Exception异常的方法。

优点
1. 提高用户体验
        友好的错误信息:通过统一异常处理,可以为前端或其他服务提供一致的、易于理解的错误信息,而不是直接暴露后端的堆栈跟踪或技术细节,从而提升用户体验。
        避免敏感信息泄露:统一处理可以避免在异常响应中不小心泄露敏感信息,如数据库表结构、内部实现细节等。
2. 业务逻辑和异常处理逻辑解耦
        代码结构清晰:将异常处理逻辑从业务逻辑中分离出来,使得业务代码更加专注于业务逻辑的实现,而异常处理代码则专注于异常的捕获、处理和响应。
        易于维护:当需要修改异常处理逻辑时,只需在统一异常处理类中修改,而无需在多个业务代码中逐一修改,降低了维护成本。
3. 减少冗余代码
        避免重复编写:在传统的Java开发中,异常处理通常是分散在代码的各个部分中的。而使用统一异常处理可以避免在每个可能抛出异常的地方编写重复的异常处理代码。
        提高代码复用性:通过定义全局的异常处理类,可以实现对多种异常的统一处理,提高代码的复用性。
4. 便于代码风格统一
        风格一致:统一异常处理有助于保持代码风格的一致性,使得整个项目的代码更加规范、整洁。
        提升可读性:对于其他开发者来说,阅读和理解统一异常处理的代码也更为容易。
5. 便于异常监控和日志记录
        集中监控:通过统一异常处理,可以更容易地实现对异常情况的集中监控和统计,为后续的故障排查和性能优化提供依据。
        详细日志:在异常处理过程中,可以记录详细的异常信息和上下文信息,便于后续的日志分析和问题定位。

Read more

Claude Sonnet 4.6:大语言模型架构演进与前沿性能评估

Claude Sonnet 4.6:大语言模型架构演进与前沿性能评估

由于官网对中国的限制,国内无法使用官网,但是使用AIGCBAR镜像站可以注册使用Claude 4.6,且比使用官网要划算,无法律风险。 1 引言:大语言模型发展的新纪元 人工智能领域正在经历一场深刻的变革,大语言模型(Large Language Model, LLM)作为这场变革的核心驱动力,正在以前所未有的速度演进。从2022年ChatGPT的横空出世,到2025-2026年各大厂商推出的新一代模型,我们见证了人工智能从"能用"到"好用"再到"专业级"的跨越式发展。Anthropic公司于2026年2月发布的Claude Sonnet 4.6,作为Claude系列的最新成员,不仅代表了当前大语言模型技术的前沿水平,更在效率与性能的平衡上树立了新的标杆。 大语言模型的发展历程可以追溯到2017年Google提出的Transformer架构,该架构通过自注意力机制(Self-Attention Mechanism)彻底改变了自然语言处理的范式。Transformer的核心创新在于其并行化处理能力和长距离依赖建模能力,这为后续大规模预训练模型的诞生奠定了理论基础。从GPT系列的

By Ne0inhk
告别复杂 SQL 性能瓶颈!金仓智能下推技术的实战解析

告别复杂 SQL 性能瓶颈!金仓智能下推技术的实战解析

你是否遇到过这样的场景:一个看似逻辑清晰的复杂SQL,在测试环境小数据量下运行飞快,一到生产环境海量数据场景就直接“卡死”;查看执行计划后发现,子查询无差别扫描全量数据,生成了远超预期的巨大中间结果集,后续的JOIN、聚合、排序操作全部陷入性能泥潭,CPU跑满、内存溢出、IO居高不下成为常态? 如果你正被此类复杂SQL的性能问题困扰,那么金仓数据库(KingbaseES)的「基于代价的连接条件下推」技术,将成为破解这一难题的关键方案。它并非简单的规则化优化,而是融合语义安全判定与智能代价评估的现代化查询优化能力,更是应对企业级复杂业务查询的“性能终结者”,让开发者和DBA彻底告别SQL性能调优的焦虑。 一、为什么你的复杂SQL会“爆内存”? 在金融、政务、零售、制造等企业级复杂业务系统中,为了保证业务逻辑的可读性和可维护性,开发人员通常会采用子查询/CTE封装复杂计算+外层JOIN过滤的SQL编写模式,将去重、聚合、窗口计算等操作封装在子查询中,外层仅做关联和最终的条件过滤。这种写法在业务层面无可厚非,却在执行层面埋下了严重的性能隐患。 1.1 典型复杂SQL的“性能陷阱”

By Ne0inhk

Clawdbot部署实操:解决‘gateway token missing’授权问题的完整步骤

Clawdbot部署实操:解决‘gateway token missing’授权问题的完整步骤 1. Clawdbot是什么:一个开箱即用的AI代理网关平台 Clawdbot 是一个统一的 AI 代理网关与管理平台,它的核心目标很实在——让开发者不用反复折腾模型对接、权限配置和会话管理,就能快速把自主AI代理跑起来、管起来、用起来。 它不是另一个大模型推理框架,而是一个“中间层操作系统”:一边连着本地或远程的AI模型(比如你熟悉的 qwen3:32b),另一边面向终端用户,提供聊天界面、会话追踪、模型路由、插件扩展等一整套能力。你可以把它理解成AI世界的“路由器+控制台+仪表盘”三合一工具。 特别适合这些场景: * 你想在内网或私有GPU上部署多个模型,但不想每个都单独写API服务; * 你需要给非技术人员(比如产品、运营)提供一个能直接对话的界面,而不是让他们敲curl命令; * 你正在做AI Agent原型验证,需要快速切换模型、调试提示词、查看token消耗; * 你希望所有AI调用都有统一日志、

By Ne0inhk
Spring Boot 实战:MyBatis 操作数据库(上)

Spring Boot 实战:MyBatis 操作数据库(上)

—JavaEE专栏— Spring Boot 实战:MyBatis 操作数据库(上) 摘要 本文深度解析了 Spring Boot 环境下 MyBatis 的集成与应用。通过回顾传统 JDBC 的局限性,详细展示了 MyBatis 在日志配置、CRUD 操作、自增主键返回及多表查询中的实战用法。同时,文章深入探讨了 #{} 与 ${} 的底层预编译差异及安全风险,并分享了企业级开发中的数据库命名规范与 Druid 连接池配置,助力开发者构建稳健的持久层架构。 文章目录 * Spring Boot 实战:MyBatis 操作数据库(上) * 摘要 * @[toc] * 1. 为什么持久层开发需要 MyBatis? * 1.1 传统 JDBC 的局限性 * 1.2

By Ne0inhk