java受检异常不会自动进入@controlleradvice,必须转为runtimeexception子类(如businessexception)或通过handlerexceptionresolver手动处理,否则会被静默吞掉或编译拦截。

Java受检异常(Checked Exception)不能像运行时异常那样自动向上冒泡到@ControllerAdvice,必须显式参与流转,否则会在编译期被拦截或在某一层被静默吞掉。要实现统一切面处理,核心不是“让throws透传”,而是让受检异常最终以Spring能识别的方式抵达全局处理器。
明确受检异常的特殊性
IOException、SQLException等继承自Exception但非RuntimeException的异常,Java编译器强制要求:调用方必须try-catch或声明throws。这意味着它们天然不会“自动”跑到@ControllerAdvice里——Spring不关心你有没有写throws,只关心最终抛到DispatcherServlet的是什么类型。
- 如果你在Service层throws IOException,但Controller层写了try-catch并吞掉,那全局处理器永远收不到
- 如果你在Controller层声明throws IOException,Spring MVC默认不会捕获它,也不会触发@ExceptionHandler(除非你额外配置HandlerExceptionResolver支持Exception基类)
- Spring Boot默认的ErrorMvcAutoConfiguration只处理未映射的错误路径(/error),不接管受检异常的JSON响应逻辑
推荐做法:统一转为运行时异常
最主流、最稳妥的方式是把受检异常在进入业务主干前就转换成RuntimeException子类(如CustomException),这样它就能自然融入Spring的异常处理链。
- 在DAO或Service底层调用处包裹原始受检异常:catch后new一个业务异常,带原始cause,再throw
- 确保新异常继承RuntimeException,无需throws声明,Controller可直接调用不处理
- 例如:new BusinessException("文件读取失败", e);其中BusinessException extends RuntimeException
- 这样@ControllerAdvice里的@ExceptionHandler
就能精准捕获并返回统一JSON格式
若坚持保留受检异常,需手动接入异常解析器
如果因历史原因或框架约束必须暴露受检异常(如集成老SDK),可通过实现HandlerExceptionResolver来补位。
- 编写一个@Component类,实现HandlerExceptionResolver接口
- 在resolveException方法中判断ex instanceof IOException,然后构造统一响应体(如Message.fail("IO操作异常"))
- 注意设置response状态码(如response.setStatus(500)),并手动写入JSON(借助ObjectMapper)
- 该方式绕过了@ControllerAdvice机制,属于低层扩展,维护成本略高
避免常见陷阱
很多团队踩坑是因为混淆了“声明throws”和“实际抛出”的区别。
- 不要在Service层throws却不在Controller层继续抛出——这等于中途截断,异常止步于Controller签名,根本没进Spring调度流程
- 不要在Controller里写catch (Exception e) { log.info(...); }却不re-throw——这是典型的“吃异常”,全局处理器收不到任何信号
- @ControllerAdvice默认不处理Exception.class,除非你显式写@ExceptionHandler(Exception.class),且需确认其优先级和匹配顺序
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











