JAVA中Spring全局异常处理@ControllerAdvice解析
一、@ControllerAdvice基础概念
1. 什么是@ControllerAdvice?
@ControllerAdvice
是Spring 3.2引入的注解,用于定义全局控制器增强组件,主要功能包括:
- 全局异常处理(最常用)
- 全局数据绑定
- 全局数据预处理
2. 核心作用
- 集中处理控制器层异常
- 避免重复的异常处理代码
- 统一API错误响应格式
- 减少
try-catch
块污染业务代码
二、基础全局异常处理实现
1. 最小实现示例
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)@ResponseBodypublic ResponseEntity<ErrorResponse> handleException(Exception ex) {ErrorResponse error = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(),"服务器内部错误",ex.getMessage());return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);}
}// 统一错误响应DTO
@Data
@AllArgsConstructor
class ErrorResponse {private int status;private String error;private String message;private long timestamp = System.currentTimeMillis();
}
2. 处理特定异常
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) {ErrorResponse error = new ErrorResponse(HttpStatus.NOT_FOUND.value(),"资源未找到",ex.getMessage());return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
三、高级配置技巧
1. 限定控制器范围
// 只处理指定包下的控制器
@ControllerAdvice(basePackages = "com.example.web.controllers")// 只处理带有特定注解的控制器
@ControllerAdvice(annotations = RestController.class)// 只处理指定类
@ControllerAdvice(assignableTypes = {UserController.class, ProductController.class})
2. 异常处理优先级
Spring会按照最具体到最通用的顺序匹配@ExceptionHandler
:
@ExceptionHandler(FileUploadException.class) // 优先匹配
public ResponseEntity<?> handleFileUpload(FileUploadException ex) { ... }@ExceptionHandler(IOException.class) // 次级匹配
public ResponseEntity<?> handleIO(IOException ex) { ... }@ExceptionHandler(Exception.class) // 兜底处理
public ResponseEntity<?> handleGeneral(Exception ex) { ... }
3. 获取请求上下文
通过注入请求对象获取更多信息:
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex, WebRequest request) {String path = ((ServletWebRequest)request).getRequest().getRequestURI();ErrorResponse error = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(),"处理请求[" + path + "]时发生错误",ex.getMessage());return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
四、常见异常处理模式
1. 业务异常处理
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {ErrorResponse error = new ErrorResponse(ex.getErrorCode(),ex.getErrorType(),ex.getMessage());return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
2. 数据校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException ex) {List<String> errors = ex.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.toList());ErrorResponse error = new ErrorResponse(HttpStatus.BAD_REQUEST.value(),"参数校验失败",errors.toString());return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
3. 认证授权异常
@ExceptionHandler({AccessDeniedException.class,AuthenticationException.class
})
public ResponseEntity<ErrorResponse> handleAuthException(RuntimeException ex) {HttpStatus status = ex instanceof AccessDeniedException ? HttpStatus.FORBIDDEN : HttpStatus.UNAUTHORIZED;ErrorResponse error = new ErrorResponse(status.value(),"权限不足",ex.getMessage());return new ResponseEntity<>(error, status);
}
五、最佳实践建议
1. 异常分类处理
建议将异常分为几大类分别处理:
异常类型 | 处理方式 | HTTP状态码 |
---|---|---|
业务异常 | 返回具体错误信息 | 400-499 |
系统异常 | 记录日志,返回通用错误 | 500 |
第三方服务异常 | 记录日志,返回服务不可用 | 503 |
参数校验异常 | 返回详细校验错误 | 400 |
2. 日志记录策略
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex, HttpServletRequest request) {log.error("请求[{} {}]处理失败", request.getMethod(), request.getRequestURI(), ex);// ...返回错误响应
}
3. 生产环境与开发环境差异
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex,Environment env) {boolean isProd = Arrays.asList(env.getActiveProfiles()).contains("prod");String message = isProd ? "服务器错误,请联系管理员" : ex.getMessage();// ...返回错误响应
}
六、原理深度解析
1. 实现原理
@ControllerAdvice
的工作原理:
- 通过
@ControllerAdvice
标记的类会被ExceptionHandlerExceptionResolver
识别 - Spring初始化时会收集所有
@ExceptionHandler
方法 - 当控制器抛出异常时,DispatcherServlet会:
- 查找当前控制器内的
@ExceptionHandler
方法 - 如果没有,则查找
@ControllerAdvice
中的处理方法 - 按异常类型匹配最具体的方法
- 查找当前控制器内的
2. 核心组件协作
[DispatcherServlet]|v
[HandlerExceptionResolver]||-- [ExceptionHandlerExceptionResolver] (处理@ExceptionHandler)|-- [ResponseStatusExceptionResolver] (处理@ResponseStatus)`-- [DefaultHandlerExceptionResolver] (处理Spring标准异常)
3. 执行顺序
@ExceptionHandler
方法(当前控制器内)@ControllerAdvice
中的@ExceptionHandler
方法@ResponseStatus
异常- Spring默认异常处理
七、常见问题解决方案
1. 异常处理不生效
可能原因:
- 未启用注解驱动:确保配置了
@EnableWebMvc
或<mvc:annotation-driven/>
- 包扫描问题:
@ControllerAdvice
类未被Spring管理 - 异常被捕获:上游代码已经catch了异常
2. 处理多个相同类型异常
@ExceptionHandler({IllegalArgumentException.class, IllegalStateException.class})
public ResponseEntity<?> handleIllegal(RuntimeException ex) {// 统一处理多种相似异常
}
3. 自定义异常解析器
如需更复杂控制,可以实现HandlerExceptionResolver
:
@Component
public class CustomExceptionResolver implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex) {// 自定义异常处理逻辑return ...;}
}
八、Spring Boot增强支持
1. 错误属性配置
application.properties
:
server.error.include-message=always
server.error.include-stacktrace=on_param
server.error.include-binding-errors=always
2. 自定义ErrorController
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class CustomErrorController implements ErrorController {@RequestMappingpublic ResponseEntity<ErrorResponse> handleError(HttpServletRequest request) {HttpStatus status = getStatus(request);ErrorResponse error = new ErrorResponse(status.value(),"自定义错误处理",(String)request.getAttribute("javax.servlet.error.message"));return new ResponseEntity<>(error, status);}private HttpStatus getStatus(HttpServletRequest request) {Integer code = (Integer) request.getAttribute("javax.servlet.error.status_code");return code != null ? HttpStatus.valueOf(code) : HttpStatus.INTERNAL_SERVER_ERROR;}
}
九、性能优化建议
- 减少
@ControllerAdvice
扫描范围:精确指定basePackages - 避免在异常处理中进行IO操作:如数据库写入
- 使用响应缓存:对相同异常返回缓存响应
- 异步异常处理:对耗时处理使用
@Async
@ExceptionHandler(ReportableException.class)
@Async
public CompletableFuture<ResponseEntity<?>> handleReportable(ReportableException ex) {// 异步处理可报告异常reportService.sendReport(ex);return CompletableFuture.completedFuture(ResponseEntity.badRequest().build());
}
通过合理使用@ControllerAdvice
进行全局异常处理,可以显著提高Spring应用的健壮性和可维护性,同时为客户端提供一致的错误响应格式。