全局异常处理
使用全局异常处理来处理校验逻辑的思路很简单,首先我们需要通过@ControllerAdvice注解定义一个全局异常的处理类,然后自定义一个校验异常,当我们在Controller中校验失败时,直接抛出该异常,这样就可以达到校验失败返回错误信息的目的了。
使用到的注解
@ControllerAdvice:类似于@Component注解,可以指定一个组件,这个组件主要用于增强@Controller注解修饰的类的功能,比如说进行全局异常处理。
@ExceptionHandler:用来修饰全局异常处理的方法,可以指定异常的类型。
使用方式
- 首先我们需要自定义一个异常类
ApiException
,当我们校验失败时抛出该异常:
/**
* 自定义API异常
* Created by macro on 2020/2/27.
*/
public class ApiException extends RuntimeException {
private IErrorCode errorCode;
public ApiException(IErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
public ApiException(String message) {
super(message);
}
public ApiException(Throwable cause) {
super(cause);
}
public ApiException(String message, Throwable cause) {
super(message, cause);
}
public IErrorCode getErrorCode() {
return errorCode;
}
}
- 然后创建一个断言处理类
Asserts
,用于抛出各种ApiException
;
/**
* 断言处理类,用于抛出各种API异常
* Created by macro on 2020/2/27.
*/
public class Asserts {
public static void fail(String message) {
throw new ApiException(message);
}
public static void fail(IErrorCode errorCode) {
throw new ApiException(errorCode);
}
}
- 然后再创建我们的全局异常处理类
GlobalExceptionHandler
,用于处理全局异常,并返回封装好的CommonResult对象;
/**
* 全局异常处理
* Created by macro on 2020/2/27.
*/
@ControllerAdvice
public class GlobalExceptionHandler {
@ResponseBody
@ExceptionHandler(value = ApiException.class)
public CommonResult handle(ApiException e) {
if (e.getErrorCode() != null) {
return CommonResult.failed(e.getErrorCode());
}
return CommonResult.failed(e.getMessage());
}
}
- 这里拿用户领取优惠券的代码为例,我们先对比下改进前后的代码,首先看Controller层代码。改进后只要Service中的方法执行成功就表示领取优惠券成功,因为领取不成功的话会直接抛出ApiException从而返回错误信息;
/**
* 用户优惠券管理Controller
* Created by macro on 2018/8/29.
*/
@Controller
@Api(tags = "UmsMemberCouponController", description = "用户优惠券管理")
@RequestMapping("/member/coupon")
public class UmsMemberCouponController {
@Autowired
private UmsMemberCouponService memberCouponService;
//改进前
@ApiOperation("领取指定优惠券")
@RequestMapping(value = "/add/{couponId}", method = RequestMethod.POST)
@ResponseBody
public CommonResult add(@PathVariable Long couponId) {
return memberCouponService.add(couponId);
}
//改进后
@ApiOperation("领取指定优惠券")
@RequestMapping(value = "/add/{couponId}", method = RequestMethod.POST)
@ResponseBody
public CommonResult add(@PathVariable Long couponId) {
memberCouponService.add(couponId);
return CommonResult.success(null,"领取成功");
}
}
- 再看下Service接口中的代码,区别在于返回结果,改进后返回的是void。其实CommonResult的作用本来就是为了把Service中获取到的数据封装成统一返回结果,改进前的做法违背了这个原则,改进后的做法解决了这个问题;
/**
* 用户优惠券管理Service
* Created by macro on 2018/8/29.
*/
public interface UmsMemberCouponService {
/**
* 会员添加优惠券(改进前)
*/
@Transactional
CommonResult add(Long couponId);
/**
* 会员添加优惠券(改进后)
*/
@Transactional
void add(Long couponId);
}
- 再看下Service实现类中的代码,可以看到原先校验逻辑中返回CommonResult的逻辑都改成了调用Asserts的fail方法来实现;
/**
* 会员优惠券管理Service实现类
* Created by macro on 2018/8/29.
*/
@Service
public class UmsMemberCouponServiceImpl implements UmsMemberCouponService {
@Autowired
private UmsMemberService memberService;
@Autowired
private SmsCouponMapper couponMapper;
@Autowired
private SmsCouponHistoryMapper couponHistoryMapper;
@Autowired
private SmsCouponHistoryDao couponHistoryDao;
//改进前
@Override
public CommonResult add(Long couponId) {
UmsMember currentMember = memberService.getCurrentMember();
//获取优惠券信息,判断数量
SmsCoupon coupon = couponMapper.selectByPrimaryKey(couponId);
if(coupon==null){
return CommonResult.failed("优惠券不存在");
}
if(coupon.getCount()<=0){
return CommonResult.failed("优惠券已经领完了");
}
Date now = new Date();
if(now.before(coupon.getEnableTime())){
return CommonResult.failed("优惠券还没到领取时间");
}
//判断用户领取的优惠券数量是否超过限制
SmsCouponHistoryExample couponHistoryExample = new SmsCouponHistoryExample();
couponHistoryExample.createCriteria().andCouponIdEqualTo(couponId).andMemberIdEqualTo(currentMember.getId());
long count = couponHistoryMapper.countByExample(couponHistoryExample);
if(count>=coupon.getPerLimit()){
return CommonResult.failed("您已经领取过该优惠券");
}
//省略领取优惠券逻辑...
return CommonResult.success(null,"领取成功");
}
//改进后
@Override
public void add(Long couponId) {
UmsMember currentMember = memberService.getCurrentMember();
//获取优惠券信息,判断数量
SmsCoupon coupon = couponMapper.selectByPrimaryKey(couponId);
if(coupon==null){
Asserts.fail("优惠券不存在");
}
if(coupon.getCount()<=0){
Asserts.fail("优惠券已经领完了");
}
Date now = new Date();
if(now.before(coupon.getEnableTime())){
Asserts.fail("优惠券还没到领取时间");
}
//判断用户领取的优惠券数量是否超过限制
SmsCouponHistoryExample couponHistoryExample = new SmsCouponHistoryExample();
couponHistoryExample.createCriteria().andCouponIdEqualTo(couponId).andMemberIdEqualTo(currentMember.getId());
long count = couponHistoryMapper.countByExample(couponHistoryExample);
if(count>=coupon.getPerLimit()){
Asserts.fail("您已经领取过该优惠券");
}
//省略领取优惠券逻辑...
}
}
- 这里我们输入一个没有的优惠券ID来测试下该功能,会返回
优惠券不存在
的错误信息。