java全局异常处理需分层覆盖:web层用@controlleradvice拦截controller异常并返回统一响应,线程层用uncaughtexceptionhandler捕获后台线程崩溃,辅以aop或事件监听器增强,关键在于按异常来源匹配机制、统一响应格式、完整记录日志。

Java 中设计全局异常处理器,核心是分层覆盖:Web 层用 @ControllerAdvice 拦截控制器异常,JVM 线程层用 UncaughtExceptionHandler 捕获未处理线程错误,两者缺一不可。
Web 请求层:用 @ControllerAdvice 统一处理 Controller 异常
这是 Spring Boot 应用最常用、最有效的全局异常捕获方式。它只作用于 MVC 控制器抛出的异常,不涉及后台线程或主线程崩溃。
- 创建一个被
@ControllerAdvice注解的类,Spring 会自动将其注册为全局异常处理器 - 用多个
@ExceptionHandler方法分别处理不同异常类型,比如NullPointerException、IllegalArgumentException、自定义的BusinessException - 每个方法返回统一结构的响应(如
ResponseEntity<errorresponse></errorresponse>),并指定对应 HTTP 状态码(400、404、500 等) - 务必保留原始异常堆栈,用日志框架记录完整
logger.error("业务异常", e),而不是只打e.getMessage()
线程执行层:用 UncaughtExceptionHandler 捕获后台线程崩溃
Web 层的 @ControllerAdvice 对非请求线程完全无效。所有异步任务、定时任务、线程池、@Async 方法,甚至主线程本身,都需靠 JVM 级别的异常处理器兜底。
- 在
main方法最开头调用Thread.setDefaultUncaughtExceptionHandler(...),确保对后续新建线程生效 - 主线程异常不会走这个默认处理器,必须额外调用
Thread.currentThread().setUncaughtExceptionHandler(...) - 线程池要自定义
ThreadFactory,保证每个线程创建时都设置 handler;不能直接用Executors.newFixedThreadPool() - Spring 的
@Async使用ThreadPoolTaskExecutor,需通过setThreadFactory注入带 handler 的工厂 - handler 内部必须轻量:只做日志输出(
logger.error("", e)或e.printStackTrace(System.err)),避免网络调用或文件写入阻塞退出
补充机制:AOP 或事件监听器按需增强
某些场景下,@ControllerAdvice 覆盖不到,需要更细粒度或跨层拦截。
- 服务层统一异常处理可借助 Spring AOP,在
@Service方法上切面捕获异常,适合审计、降级、熔断等逻辑 - AWS/Android/Swing 等特殊环境有专属机制:比如 Swing 的 EDT 异常需结合
Thread.setDefaultUncaughtExceptionHandler和System.setProperty("sun.awt.exception.handler", ...) - 不要依赖单一方案——线上常见“看似捕获成功,实则静默丢弃”,往往是因为 handler 自身抛了新异常或用了非线程安全对象(如
SimpleDateFormat)
关键设计原则
真正健壮的全局异常处理不是堆砌注解,而是明确每类异常的归属和处置路径。
- 区分异常来源:是 HTTP 请求触发?还是后台线程计算失败?或是 JVM 初始化报错?不同来源用不同机制
- 响应格式统一:前后端约定好
code、message、timestamp等字段,避免前端解析混乱 - 日志必须完整:异常堆栈、线程名、请求 ID(如有)、时间戳,缺一不可
- 禁止在 handler 中抛新受检异常,也不做耗时操作;否则可能掩盖原始问题,甚至导致进程卡死
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











