首先,在业务进行时,会对数据库进行操作的行为,因此,在业务发生异常时,必须将数据库的操作进行回滚,将数据恢复至未进行操作前,这里,我们就需要使用@Transactional注解。
这里我们可以看到,这个注解来源于Spring框架的 spring-tx 的jar包,这个包的名字说明这个包是专用来完成事务功能的。
@Transactional注解是Spring框架中用于声明事务性操作的注解。它可以应用于方法级别或类级别,并支持多种属性来定义事务的行为。当一个方法被标记为@Transactional时,Spring会在方法执行前开启一个事务,在方法执行后根据方法执行结果决定是提交事务还是回滚事务。
@Transactional
public void buy(Long id, GoodsBuyDto goodsBuyDto) {
...
}
该注解一般应用于操作多个数据库表的方法中,如在进行购买商品时,需要对数据库中的商品表,用户的余额表,订单表同时进行操作,一旦发生异常时,需要对操作进行回滚,因此需要在方法上面加上@Transactional注解用于发生异常时的操作回滚。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
...
}
以上时Transactional注解的简略代码,由@Retention(RetentionPolicy.RUNTIME)可以看出,这个注解只会应用于运行时异常,在开发业务时,会有商品库存不够,用户余额不足等异常信息,因此,我们可以定义一个继承于RuntimeException的异常类BaseException,这个异常类可以应用于业务发生异常时所抛出的Exception。
public class BaseException extends RuntimeException {
public BaseException() {
}
public BaseException(String msg) {
super(msg);
}
}
下面是使用该异常类的一个简单应用,商品状态为2时,就会抛出一个BaseException异常,信息为该商品已经被购买。
//判断商品状态
if (goods.getStatus() == 2) {
throw new BaseException(MessageConstant.ALREADY_BUY);
}
但是,在业务中,如果后端抛出异常,则会在前端报出状态码为500,即为后端服务器发生错误,会让使用者认为是服务器发生了错误,因此,我们可以定义一个全局异常处理器对BaseException 进行捕捉,优雅地处理异常并向客户端返回有意义的错误信息。这里Spring Boot 提供了 @RestControllerAdvice
注解来实现全局异常处理,让我们能够集中处理控制器层抛出的异常,统一错误响应格式,提高系统的健壮性。
/**
* 全局异常处理器,处理项目中抛出的业务异常
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 捕获业务异常
* @param ex
* @return
*/
@ExceptionHandler
public Result exceptionHandler(BaseException ex){
log.error("异常信息:{}", ex.getMessage());
return Result.error(ex.getMessage());
}
Result类是自定义的一个用于统一返回后端结果的一个类
import lombok.Data;
import java.io.Serializable;
/**
* 后端统一返回结果
* @param <T>
*/
@Data
public class Result<T> implements Serializable {
private Integer code; //编码:1成功,0和其它数字为失败
private String msg; //错误信息
private T data; //数据
public static <T> Result<T> success() {
Result<T> result = new Result<T>();
result.code = 1;
return result;
}
public static <T> Result<T> success(T object) {
Result<T> result = new Result<T>();
result.data = object;
result.code = 1;
return result;
}
public static <T> Result<T> error(String msg) {
Result result = new Result();
result.msg = msg;
result.code = 0;
return result;
}
}
这样便可以优雅的处理异常并且统一向前端返回相同格式的异常信息。