Springboot2.X 基础篇-全局异常处理
全局异常处理介绍
说到异常是项目开发中最熟悉不过的了,为了处理异常,我们往往会写很多类似于下面这样的代码:
public void hello() {
try {
// 业务代码
}catch (Exception e) {
e.printStackTrace();
}
}
这样写出的代码包含了很多异常处理,不仅仅可读性差,而且包含了需要非业务相关的逻辑。
在项目开发中我们应该将精力放在业务处理上,项目中除了业务相关的代码,最好不要存在其他无关的代码。
那么类似于异常这种项目中各个地方都存在的非业务代码如何处理呢?
首先,异常处理是必不可少的,除了不能影响用户体验和前后端交互,也是我们排查问题必不可少的关键信息。
其次,我们关注的问题是何时何地处理异常。
如果你感兴趣的话,花点时间看看下面内容吧!
全局异常处理实战
首先抛出几个概念,方便大家对后面内容的理解
- 采用的是SpringBoot 自带的异常处理机制
- 对通过Controller接口访问产生的异常,统一处理,响应结果格式统一
1、准备
(1)创建一个SpringBoot项目,并对外暴露访问接口
我准备了这样一个接口,并构造了一个 / by zero
异常,浏览器访问结果:
这样的接口你要提供出去了,看前端小姐姐不打死你 。。。
2、配置全局异常通知
在SpringBoot 2.X 版本中 @ControllerAdvice
@RestControllerAdvice
注解,可以用于定义 @ExceptionHandler
,并应用到配置了 @RequestMapping
的控制器中。
- @ControllerAdvice 注解:是一个特殊的
@Component
,用于标识一个类,这个类中被以下三种注解标识的方法:@ExceptionHandler
,@InitBinder
,@ModelAttribute
,将作用于所有的@Controller
类的接口上。 - @RestControllerAdvice 注解:是
@ControllerAdvice
+@ResponseBody
的结合体 - @ExceptionHandler 注解:统一异常处理,也可以指定要处理的异常类型
下面我们就实战一个简单的全局异常处理:
@RestControllerAdvice
@Log4j2
public class GlobalExceptionHandler {
/**
* 处理任何 Exception 接口实现类异常
*
* @param request
* @param exception
* @return
* @throws Exception
*/
@ExceptionHandler(Exception.class)
public Map exceptionHandler(HttpServletRequest request, Exception exception) throws Exception {
log.error("url:{}|errorMsg:{}", request.getRequestURI(), exception.getMessage(), exception);
Map<String, Object> result = new HashMap<>(3);
result.put("code", 500);
result.put("msg", exception.getMessage());
result.put("url", request.getRequestURL().toString());
return result;
}
}
再次访问我们的浏览器:
刚才的报错页面现在就变成了这样
到此为止,一个简单的全局异常处理解决方式就完成了,是不是非常简单,当然这只是最最简单的版本,现实项目中远远比这个复杂,下面我们就来一个全套的项目中异常如何处理的。
全局异常处理实战进阶
在真实的项目开发中,前后端一般是分离的,就表示后端不管出现什么情况(成功、失败、异常),返回给前端的数据结构一定是统一的,这是约定大于编码思想的一种体现。
1、统一接口响应
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResult<T> implements Serializable {
/**
* 响应码
*/
private Integer code;
/**
* 响应信息
*/
private String msg;
/**
* 响应数据
*/
private T data;
public static <T> CommonResult<T> success(T data) {
return new CommonResult(200, "success", data);
}
}
2、自定义异常
为什么要自定义异常呢?
项目中为什么要自定义异常呢,首先如果是项目业务处理产生的异常,通过自定义异常可以知道是哪个项目产生的异常,而且对不同的异常也可以有不同的处理逻辑,也方便后续扩展
public class CustomException extends RuntimeException {
private static final long serialVersionUID = 1L;
//自定义错误码
private Integer code;
//自定义构造器,只保留一个,让其必须输入错误码及内容
public CustomException(int code, String msg) {
super(msg);
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
3、改造全局异常处理代码
@RestControllerAdvice
@Log4j2
public class GlobalExceptionHandler {
/**
* 处理CustomException 异常
*
* @param request
* @param exception
* @return
* @throws Exception
*/
@ExceptionHandler(CustomException.class)
public Map exceptionHandler(HttpServletRequest request, CustomException exception) throws Exception {
log.error("url:{}|errorMsg:{}", request.getRequestURI(), exception.getMessage(), exception);
Map<String, Object> result = new HashMap<>(3);
result.put("code", exception.getCode());
result.put("msg", exception.getMessage());
result.put("url", request.getRequestURL().toString());
return result;
}
/**
* 处理任何 Exception 接口实现类异常
*
* @param request
* @param exception
* @return
* @throws Exception
*/
@ExceptionHandler(Exception.class)
public Map exceptionHandler(HttpServletRequest request, Exception exception) throws Exception {
log.error("url:{}|errorMsg:{}", request.getRequestURI(), exception.getMessage(), exception);
Map<String, Object> result = new HashMap<>(3);
result.put("code", 500);
result.put("msg", exception.getMessage());
result.put("url", request.getRequestURL().toString());
return result;
}
}