背景
在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);
}
}
通過上面的修改,我們可以根據自己的業務邏輯,去捕獲對應的異常,最后返回合適的實體結果給調用方。
