记一次SpringAOP环绕通知导致全局异常抓取失效的问题

背景

在web开发过程中,我们每个项目都必然会主动或者被动的抛出各种各样的异常,那么如果抛出到最上层还没有捕获,那么就会导致程序停止。所以,一般我们在开发中都会使用全局异常捕获机制,捕获各种各样的异常,最后返回统一的结果实体类给调用方。
另一方面,我们在使用spring框架开发的过程中,也会使用到aop来记录日志或者一些与业务无关的信息。我在使用aop的环绕通知记录接口请求时间时,遇到全局异常处理失效的问题。 导致前端在调用结束后,接口请求正常,但是却获取不到全局异常处理的结果。

全局异常处理代码

/**
 * 全局捕获异常的类,返回信息给浏览器,可以自定义返回的code,msg等信息
 */
@ControllerAdvice
public class ApiExceptionHandle {
    private static final Logger logger = LoggerFactory.getLogger(ApiExceptionHandle.class);

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ApiResult handle(Exception e) {
        logger.error("全局异常:{},{}", e.getMessage(), e);
        return ResultUtil.handleApiException(e);
    }
}

AOP记录接口请求时间的代码

@Aspect
@Service
public class SubReqAop {
    private static final Logger logger = LoggerFactory.getLogger(SubReqAop.class);

    @Autowired
    private SubReqDomainService subReqDomainService;

    //切入所有以schedule开头的方法,计算排程时间
    @Pointcut("execution(* com.hierway.schedm.schedService.impl.ScheduleBusinessImpl.schedule*(..))")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        long startTime = System.currentTimeMillis();
        Object proceed = null;
        try {
            proceed = joinPoint.proceed(args);//调用业务代码
            long endTime = System.currentTimeMillis();
            ApiResult result = (ApiResult) proceed;
            if (ResultCode.SUCCESS.getCode().equals(result.getCode())) {
                SubreqDoneinfo build = SubreqDoneinfo.builder().createtime(new Date()).spendtime((int) (endTime - startTime)).build();
                subReqDomainService.insertDoneInfo(build);
            }
        }catch (Throwable throwable){
            throwable.printStackTrace();
        }
        return proceed;
    }

}

上面通过proceed调用业务逻辑代码,因为业务逻辑中可能会抛出自定义异常,如:参数错误。如果调用过程中出现异常,将导致proceed返回的给调用方的值为null。

修改之后的AOP记录接口请求时间的代码

/**
 * @ClassName: SubReqAop
 * @Description: 记录Schedule花费多长时间的AOP
 * @Author: sunyiwei
 * @Date: 2019/9/10 15:23
 */
@Aspect
@Service
public class SubReqAop {
    private static final Logger logger = LoggerFactory.getLogger(SubReqAop.class);

    @Autowired
    private SubReqDomainService subReqDomainService;

    //切入所有以schedule开头的方法,计算排程时间
    @Pointcut("execution(* com.hierway.schedm.schedService.impl.ScheduleBusinessImpl.schedule*(..))")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        long startTime = System.currentTimeMillis();
        Object proceed = null;
        try {
            proceed = joinPoint.proceed(args);
            long endTime = System.currentTimeMillis();
            ApiResult result = (ApiResult) proceed;
            if (ResultCode.SUCCESS.getCode().equals(result.getCode())) {
                SubreqDoneinfo build = SubreqDoneinfo.builder().createtime(new Date()).spendtime((int) (endTime - startTime)).build();
                subReqDomainService.insertDoneInfo(build);
            }
            return proceed;
        } catch (ApiException e) {
            //如果是自定义的异常都将捕获,并通过handleApiException方法转换为对应的实体类
            logger.error("SubReqAop异常了");
            e.printStackTrace();
            return ResultUtil.handleApiException(e);
        }catch (Throwable throwable){
            throwable.printStackTrace();
        }
        return ResultUtil.error(ResultCode.UNKONW_ERROR);
    }


}

通过上面的修改,我们可以根据自己的业务逻辑,去捕获对应的异常,最后返回合适的实体结果给调用方。

posted @ 2020-09-29 18:07  奕为  阅读(3273)  评论(0编辑  收藏  举报