Java异常全局捕获核心是统一处理未显式捕获的系统异常,区分业务异常(不归全局管)、系统异常(需脱敏记录)、线程/异步异常(需单独配置);Spring Boot中主流用@ControllerAdvice+@ExceptionHandler分层处理,生产禁用堆栈暴露,统一响应体封装,并补充JVM级兜底与线程池异常管理。

Java 异常全局捕获设计,核心是统一处理未显式捕获的异常,避免程序崩溃、日志混乱或敏感信息泄露。面试中考察的是你对异常分类、拦截时机、框架机制和工程落地的理解,不是写个 try-catch 就完事。
明确全局捕获的边界:哪些异常能捕?哪些不能?
全局捕获 ≠ 捕获所有异常。关键要区分:
- 业务异常(如 OrderNotFoundException):通常不归全局捕获管,应由 Controller 层或 Service 层主动 throw + @ExceptionHandler 处理,保持语义清晰
- 系统异常(如 NullPointerException、IllegalArgumentException):属于编程错误或非法输入,适合在全局处理器中统一记录、脱敏、返回友好提示
- 线程内未捕获异常(new Thread(() -> {...}).start() 中抛出的):需通过 Thread.setDefaultUncaughtExceptionHandler 设置,和 Web 层的全局异常处理器无关
- 异步任务异常(@Async 方法):默认丢失,必须配合 AsyncConfigurer 自定义 TaskExecutor 并设置异常处理器,或用 CompletableFuture.exceptionally() 显式处理
Spring Boot 下主流实现方式:@ControllerAdvice + @ExceptionHandler
这是 Web 场景最常用、最规范的做法。重点不是“写了没”,而是设计是否健壮:
- 按异常类型分层处理:为不同异常(如 BusinessException、ValidationException、RuntimeException)提供不同响应结构和 HTTP 状态码
- 避免暴露堆栈给前端:生产环境只返回错误码和简明消息,堆栈记录到日志(用 log.error("xxx", e))
- 统一响应体封装:返回标准格式如 { "code": 500, "msg": "服务器忙,请稍后再试", "data": null }
- 注意 @ExceptionHandler 的生效范围:它只对 Controller 层抛出的异常有效;Service 层抛出但被 Controller 吞掉的异常,不会触发
非 Web 场景补充:JVM 级别兜底与线程池异常管理
后台服务、定时任务、消息消费等场景,不能只依赖 Spring MVC 的机制:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 设置 JVM 全局未捕获异常处理器:
Thread.setDefaultUncaughtExceptionHandler(),用于主线程外的裸线程 - 自定义 ThreadPoolTaskExecutor:重写
decorateTask或设置setRejectedExecutionHandler,确保线程池任务异常可追溯 - 消息中间件(如 Kafka Listener)需单独配置 error handler,例如
DefaultErrorHandler+SeekToCurrentErrorHandler - 避免在 finally 块里吞掉异常,尤其不要写
catch (Exception e) { }—— 这会让全局捕获失效且难以排查
面试表达建议:说清取舍和理由,比代码更重要
面试官想听的是你的设计权衡。例如:
- “我没用 try-catch 包裹所有 service 调用,因为会污染业务逻辑、掩盖真实调用链;而是靠全局处理器兜底 + 单元测试覆盖空指针场景”
- “对参数校验异常,我单独写了一个 @ExceptionHandler(MethodArgumentNotValidException.class),返回 400 和字段级错误,而不是混在 RuntimeException 里统一处理”
- “线上禁用 printStackTrace(),所有异常都走 SLF4J 的 error 方法,并附加 traceId,方便日志平台关联追踪”
不复杂但容易忽略。真正好的全局异常设计,是让错误可发现、可定位、可收敛,而不是让代码看起来“没报错”。

















