记一次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);
}
}
通过上面的修改,我们可以根据自己的业务逻辑,去捕获对应的异常,最后返回合适的实体结果给调用方。