点击上方蓝色字体关注我吧
一起学习,一起进步,做积极的人!
前言
《Spring Boot 快速入门系列》上一节「接口规范篇」讲完了,小伙伴们是否已经掌握了基本的接口编写规范(后面会有一篇专门演示在线接口文档内容)。
Spring Boot 快速入门系列:
Spring Boot 快速入门系列(先导篇) —— 从 Hello World 开始
Spring Boot 快速入门系列(I) —— 属性配置篇
Spring Boot 快速入门系列(II)—— 数据操作篇之 Spring Data JPA
Spring Boot 快速入门系列(III)—— 数据操作篇之 JdbcTemplate
Spring Boot 快速入门系列(IV)—— 数据操作篇之 MyBatis
Spring Boot 快速入门系列(V)—— 事务管理篇之 @Transactional
Spring Boot 快速入门系列(VI)—— 接口规范篇
对于一个接口服务来说,异常处理显得尤为重要,因为没有任何程序是没有 bug 的。那么在程序可能出现异常的地方,需要我们手动捕获和处理异常。比如像下面这 3 种方式见的很多:
try {
// 可能发生异常的逻辑
...
doSomething();
} catch (ExceptionType e) {
// 异常处理
logger.error(e);
throw new MyException(e.getMessage());
}
// or
try {
// 可能发生异常的逻辑
...
doSomething();
} catch (ExceptionType1 e1) {
// 异常处理
logger.error(e1);
throw new MyException(e1.getMessage());
} catch (ExceptionType2 e2) {
// 异常处理
logger.error(e2);
throw new MyException(e2.getMessage());
}
// or
try {
// 可能发生异常的逻辑
...
doSomething();
} catch (ExceptionType1 e1 | ExceptionType2 e2) {
// 异常处理
logger.error(e1 + "\n" + e2);
throw new MyException(e1.getMessage() + e2.getMessage());
}
像这种标准的 try { ... } catch(ExceptionTypes e) { ... } 是处理异常的常见解决问题的方式,但是实际程序我们不可能每一段代码都会 try-catch 处理,还有一些异常可能无法提前预知,没有显式的捕获处理,那如果此时程序发生异常,我们该如何处理呢。
那么今天我们就来讲讲对于基于 Spring Boot 构建的项目中如何处理这些异常问题的。
异常处理
1. @ExceptionHandler 注解
了解 SpringMVC 或 Spring Boot 框架的人应该知道 @ExceptionHandler 这个异常处理注解,我们可以使用这个注解来捕获程序异常。
1.1 单一 Action/Controller 异常处理
这种使用 @ExceptionHandler 注解的场景较少,但作为学习异常处理还是非常不错的,只需在对应的 xxxAction / xxxController 控制层增加一个异常处理的方法,并使用 @ExceptionHandler 注解标识即可。
@ExceptionHandler(Exception.class)
public Result handleException() {
// 统一返回数据格式:《Spring Boot 快速入门之参数规范篇》的示例代码
// 以下相同
return new ResponseDataVO(ResponseStatusCode.CUSTOMEXCEPTION);
}
1.2 基类 Action/Controller 异常处理
在应用广泛的 SSH 或是 SSM 框架项目中,一般存在着多个 xxxAction / xxxController 控制层,而它们在异常处理方面又存在着很多的共性,这样就不太适合在每个 xxxAction / xxxController 类中都嵌入一个对应的异常处理方法。因此我们可以将异常处理方法抽取到一个统一继承的基类中,然后所有的 xxxAction / xxxController 统一继承基类即可。
基类 BaseAction / BaseController:
public class BaseAction {
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
return new ResponseDataVO(ResponseStatusCode.CUSTOMEXCEPTION);
}
}
// or
public class BaseController {
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
return new ResponseDataVO(ResponseStatusCode.CUSTOMEXCEPTION);
}
}
UserController 通过继承 BaseAction / BaseController 完成异常处理:
@RestController@RequestMapping("/sys")
public class LoginController extends BaseAction {
@PostMapping("/login")
public ResponseDataVO login(@RequestBody UserLoginReqVO userLoginReqVO) {
// 具体业务处理省略
...
doSomething();
}
}
// or
@RestController
@RequestMapping("/sys")
public class LoginController extends BaseController {
@PostMapping("/login")
public ResponseDataVO login(@RequestBody UserLoginReqVO userLoginReqVO) {
// 具体业务处理省略
...
doSomething();
}
}
2. @ControllerAdvice 注解
对于使用基类 BaseAction / BaseController 完成统一异常处理也存在着缺点,那就是代码耦合度高,一旦开发人员编写 xxxAction / xxxController 时忘记继承 BaseAction / BaseController 基类,发生异常时又会直接抛到用户面前。如若想去除异常捕获的代码耦合度,我们可以使用 @ControllerAdvice 和 @ExceptionHandler 注解结合完成全局异常处理。
@ControllerAdvice
@ResponseBody
public class ExceptionHandlerAdvice {
@ExceptionHandler(Exception.class)
public ResponseDataVO handleException(Exception e) {
return new ResponseDataVO(ResponseStatusCode.CUSTOMEXCEPTION);
}
}
在该类中,可以定义多个类别的异常捕获方法,不同的方法用于捕获不同类别的异常情况,例如专门捕获 SQL 异常的方法、专门捕获自定义异常的方法等等,也可以直接使用上面代码,在一个方法中处理所有的异常信息。
另外,在 ExceptionHandlerAdvice 类中的 @ExceptionHandler 注解是用来指明用户要捕获异常类型,这样发生异常时可以精确匹配到该方法,即如果这里指定为自定义异常类 CustomException,则其他异常就不会进到捕获自定义异常的方法中。
例如:
自定义异常类 BusinessException,代码如下:
public class BusinessException extends Exception {
//业务类型
private String bizType;
//业务代码
private int bizCode;
//错误信息
private String message;
public BusinessException(String bizType, int bizCode, String message){
super(message);
this.bizType = bizType;
this.bizCode = bizCode;
this.message = message;
}
public BusinessException(String message){
super(message);
this.bizType = "";
this.bizCode = -1;
this.message = message;
}
public BusinessException(String bizType, String message){
super(message);
this.bizType = bizType;
this.bizCode = -1;
this.message = message;
}
public BusinessException(int bizCode, String message){
super(message);
this.bizType = "";
this.bizCode = bizCode;
this.message = message;
}
// 省略 getter/setter 方法
}
ExceptionHandlerAdvice 类中新增 businessException 方法捕获自定义异常,代码如下:
@ControllerAdvice
@ResponseBody
public class ExceptionHandlerAdvice {
@ExceptionHandler(Exception.class)
public ResponseDataVO handleException(Exception e) {
return new ResponseDataVO(ResponseStatusCode.CUSTOMEXCEPTION);
}
// 自定义异常捕获
@ExceptionHandler(BusinessException.class)
public Result businessException(BusinessException e) {
return new ResponseDataVO(ResponseStatusCode.CUSTOMEXCEPTION);
}
}
小结
通过以上的学习,大家应该了解了 @ExceptionHandler 和 @ControllerAdvice 这两个注解的用法,对于全局异常处理有了一定的认识,为了方便对异常的统一管理,SpringMVC、Spring Boot 提供了 @ControllerAdvice 注解对异常进行统一的处理,拿到这些异常信息后,可以做一些处理,比如提供一个统一的 Web 界面查看异常信息,或者捕捉到异常信息后,以发送短信或是邮件形式通知到相关技术人员,可以帮助其开发人员快速发现并定位问题,减少以往通过查看线上日志文件排查问题繁琐过程的时间。而 @ControllerAdvice 注解的作用远远不止这些,还包括全局数据绑定和全局数据预处理等,有兴趣想了解更多相关信息的可以去 Spring 官方文档中查阅学习。
——> End <——
# 精彩推荐 #
JVM故障分析及性能优化实战(I)——使用jstack定位线程堆栈信息
我 "在看",你呢