引言
在开发基于Spring Boot的应用时,异常是常见的。优雅地处理异常不仅可以提升应用的健壮性,还能让开发人员快速定位到BUG。虽然SpringBoot框架有部分的异常处理了,但是对于我们开发者而言BUG提示信息太广,定位不准,因此我们需要对这些异常进行统一的捕获并处理。
未使用全局异常:我们只能看出是服务器的问题。
使用全局异常:可以看出改错误是在创建培训资源时发生的错误。
一. 注解介绍
Spring Boot提供了多种方式来处理全局异常,其中最常见和推荐的方式是使用@ControllerAdvice或@RestControllerAdvice注解。(注:@RestControllerAdvice是一个组合注解,由@ControllerAdvice、@ResponseBody组成)
官方文档:
Controller Advice
Typically, the @ExceptionHandler, @InitBinder, and @ModelAttribute methods apply within the @Controller class (or class hierarchy) in which they are declared. If you want such methods to apply more globally (across controllers), you can declare them in a class annotated with @ControllerAdvice or @RestControllerAdvice.
@ControllerAdvice is annotated with @Component, which means that such classes can be registered as Spring beans through component scanning . @RestControllerAdvice is a composed annotation that is annotated with both @ControllerAdvice and @ResponseBody, which essentially means @ExceptionHandler methods are rendered to the response body through message conversion (versus view resolution or template rendering).
翻译:
通常,@ExceptionHandler、@InitBinder 和 @ModelAttribute 方法应用于它们被声明的 @Controller 类(或类层次结构)中。如果你想让这些方法更全局地应用(跨控制器),你可以将它们声明在一个用 @ControllerAdvice 或 @RestControllerAdvice 注解的类中。
@ControllerAdvice 被 @Component 注解,这意味着这样的类可以通过组件扫描注册为 Spring Bean。@RestControllerAdvice 是一个组合注解,它同时被 @ControllerAdvice 和 @ResponseBody 注解,这基本上意味着 @ExceptionHandler 方法通过消息转换(而不是视图解析或模板渲染)将结果渲染到响应体中。
二. Spring Boot中的全局异常处理
1.创建一个自定义的异常类(这一步是可选的,但推荐用于区分不同类型的错误,细化BUG)
public static class ErrorMessage {
private String message;
private int status;
private List<String> errors = new ArrayList<>();
@JsonIgnore
private final HttpStatus httpStatus;
@JsonIgnore
private final GetErrors getErrorsFn;
// get set 及构造函数省略
}
2.创建一个使用@RestControllerAdvice注解的类,用于全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
private final HashMap<Class<? extends Exception>, ErrorMessage> exceptionStatusMap = new HashMap<Class<? extends Exception>, ErrorMessage>();
public GlobalExceptionHandler() {
// 加入自定义的异常
this.exceptionStatusMap.put(Exception.class, new ErrorMessage("exception", HttpStatus.SERVICE_UNAVAILABLE, 50000));
this.exceptionStatusMap.put(RuntimeException.class, new ErrorMessage("runtime exception", HttpStatus.SERVICE_UNAVAILABLE, 50001));
}
/**
* runtime异常.
*
* @param request 请求
* @param exception 异常
* @return 异常处理器
*/
@ExceptionHandler(value = Exception.class)
public ResponseEntity<ErrorMessage> runtimeExceptionHandler(HttpServletRequest request, Exception exception) {
if (exception.getClass().equals(RuntimeException.class) || exception.getClass().equals(Exception.class)) {
logger.error("程序运行异常: 主机 {} 调用地址 {} 错误信息 {}",
request.getRemoteHost(), request.getRequestURL(), exception.getMessage());
exception.printStackTrace();
}
ErrorMessage errorMessage;
if (!this.exceptionStatusMap.containsKey(exception.getClass())) {
logger.warn("未找到" + exception.getClass() + "的异常处理器,请添加");
if (exception instanceof RuntimeException) {
errorMessage = this.exceptionStatusMap.get(RuntimeException.class);
} else {
errorMessage = this.exceptionStatusMap.get(Exception.class);
}
this.logger.warn("异常信息", errorMessage.getMessage());
exception.printStackTrace();
} else {
errorMessage = this.exceptionStatusMap.get(exception.getClass());
}
return new ResponseEntity<>(errorMessage.addException(exception), errorMessage.httpStatus);
}
}
三.注解@ControllerAdvice的工作原理
Spring MVC : 注解@ControllerAdvice的工作原理
参考文章
spring 官网
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。