Java中设计全局异常处理器需分层处理:@ControllerAdvice拦截控制器层业务异常并统一响应格式;UncaughtExceptionHandler兜底线程崩溃;AWT/JVM启动异常需专项配置,无法被常规handler捕获。

Java 中设计全局异常处理器捕获“未处理异常”,关键在于区分两类场景:一类是 Web 控制器层的业务异常(可用 @ControllerAdvice 拦截),另一类是真正意义上的“未捕获”异常——比如线程崩溃、主线程抛错、异步任务、AWT 事件或 JVM 启动阶段的问题。单靠一种方式无法覆盖全部,必须分层配置。
Web 层统一响应:用 @ControllerAdvice 处理控制器异常
这是最常用也最有效的方案,适用于 Spring Boot 项目中所有 @RestController 或 @Controller 方法抛出的异常:
- 定义一个返回结构体(如
Result<T>),含状态码、消息、时间戳和数据字段 - 创建被
@ControllerAdvice注解的类,在其中用多个@ExceptionHandler分别处理MyException、IllegalArgumentException、Exception等 - 注意它不处理 404、405 这类 HTTP 状态异常,需额外配置
server.error.include-message=always或自定义ErrorController - 敏感信息要脱敏,日志记录必须保留完整堆栈(不能只打
e.getMessage())
线程级兜底:用 UncaughtExceptionHandler 捕获崩溃线程
当线程内未 catch 的异常发生时(如 new Thread(() -> { int x = 1/0; }).start();),JVM 会调用其未捕获异常处理器:
- 主线程异常不会走
setDefaultUncaughtExceptionHandler,必须单独对Thread.currentThread()设置 - 线程池创建的线程需通过自定义
ThreadFactory统一注入 handler,不能依赖Executors.defaultThreadFactory() - Spring 的
@Async使用ThreadPoolTaskExecutor,要重写其setThreadFactory属性 - handler 内部避免耗时操作(如网络上报、文件写入),推荐只做轻量日志(
logger.error("", e))
特殊环境补充:AWT/Swing 和 JVM 启动异常
桌面应用或底层框架启动失败时,常规 handler 无效,需针对性处理:
立即学习“Java免费学习笔记(深入)”;
- AWT 事件分发线程(EDT)异常容易导致界面冻结,应在 main 开头设置
Thread.setDefaultUncaughtExceptionHandler,并配合System.setProperty("sun.awt.exception.handler", "xxx")(仅 Oracle/Sun JVM 有效) - 更可靠的做法是在每个事件监听器(如
actionPerformed)里加try-catch,主动弹窗提示用户 - JVM 启动阶段(如
static块报错、类加载失败)无法被任何 Java 层 handler 捕获,只能靠外部监控或容器日志排查
不推荐但偶见的替代方案:AOP 切面捕获
用 @Aspect 在 service 或 controller 方法执行后捕获抛出的异常,适合做日志审计或指标上报:
- 优势是能精确控制切点(如只拦截
com.example.controller..*) - 缺点是无法替代
@ControllerAdvice返回 HTTP 响应,也不能捕获过滤器、拦截器、异步回调中的异常 - 若用于错误码统一封装,仍需配合
@ResponseBody+ResponseEntity手动构造返回值


















