利用spring aop統一處理異常和打日誌

spring aop的概念,很早就寫博客介紹了,現在在工作中真正使用。

我們很容易寫出的代碼

我們很容易寫出帶有很多try catch 和 logger.warn(),logger.error()的代碼,這樣一個方法本來的業務邏輯只有5行,有了這些,代碼就變成了10行或者更多行,如:

public ResultDTO<UserDTO> queryUserByCardId(String cardId) {
        ResultDTO<UserDTO> result = new ResultDTO<UserDTO>();
        StringBuilder log = new StringBuilder();
        log.append("queryUserByCardId:" + cardId);
        try {
            checkCardIdNotNull(cardId);
            StationUserDO userDO = userDAO.queryUserByCardId(cardId);
            UserDTO stationUserDTO = DataTypeConvertUtils.DOToDTO(userDO);
            result.setData(stationUserDTO);
            logger.warn(log.append(" result:").toString() + result);
        } catch (StationErrorCodeException e) {
            //logger.error(log.append("catch StationErrorCodeException!").toString(), e);
            result.setSuccess(false);
            result.setErrorCode(e.getErrorCode().getErrorCode());
            result.setErrorMessage(e.getErrorCode().getErrorMessage());
        } catch (Exception e) {
            logger.error(log.append("catch Exception!").toString(), e);
            result.setSuccess(false);
            result.setErrorCode(StationErrorCodeConstants.STA10001.getErrorCode());
            result.setErrorMessage(StationErrorCodeConstants.STA10001.getErrorMessage());
        }
        return result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

實際上,我們的業務邏輯就幾行而已,中間卻夾雜着那麼多的異常處理代碼及日誌信息代碼。


如何改進代碼

我們可以使用springaop,做一個切面,這個切面專門做記錄日誌和異常處理的工作,這樣就能減少重複代碼。 
代碼如下:

@Override
public ResultDTO<StationUserDTO>queryUserByCardId(String cardId) {
        ResultDTO<StationUserDTO> result = new ResultDTO<StationUserDTO>();
        checkCardIdNotNull(cardId);
        StationUserDO userDO = stationUserDAO.queryStationUserByCardId(cardId);
        StationUserDTO stationUserDTO = DataTypeConvertUtils.DOToDTO(userDO);
        result.setData(stationUserDTO);
        return result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

我們在切面中做異常處理和記錄日誌:

@Aspect
public class CardServiceAspect {
    private final Logger logger = LoggerFactory.getLogger("card");
    // 切入點表達式按需配置
    @Pointcut("execution(* *.*(..)))")
    private void myPointcut() {
    }

    @Before("execution(* *.*(..)))")
    public void before(JoinPoint joinPoint) {
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        logger.warn(className + "的" + methodName + "執行了");
        Object[] args = joinPoint.getArgs();
        StringBuilder log = new StringBuilder("入參爲");
        for (Object arg : args) {
            log.append(arg + " ");
        }
        logger.warn(log.toString());
    }

    @AfterReturning(value = "execution(* *.*(..)))", returning = "returnVal")
    public void afterReturin(Object returnVal) {
        logger.warn("方法正常結束了,方法的返回值:" + returnVal);
    }

    @AfterThrowing(value = "StationCardServiceAspect.myPointcut()", throwing = "e")
    public void afterThrowing(Throwable e) {
        if (e instanceof StationErrorCodeException) {
            logger.error("通知中發現異常StationErrorCodeException", e);
        } else {
            logger.error("通知中發現未知異常", e);
        }
    }

    @Around(value = "StationCardServiceAspect.myPointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        logger.warn("前置增強...");
        Object result = null;
        try {
            result = proceedingJoinPoint.proceed();
        } catch (Exception e) {
            ResultDTO resultDTO = new ResultDTO();
            if (e instanceof StationErrorCodeException) {
                StationErrorCodeException errorCodeException = (StationErrorCodeException) e;
                resultDTO.setSuccess(false);
                resultDTO.setErrorCode(errorCodeException.getErrorCode().getErrorCode());
                resultDTO.setErrorMessage(errorCodeException.getErrorCode().getErrorMessage());
            } else {
                resultDTO.setSuccess(false);
                resultDTO.setErrorCode(StationErrorCodeConstants.STA10001.getErrorCode());
                resultDTO.setErrorMessage(StationErrorCodeConstants.STA10001.getErrorMessage());
            }
            return resultDTO;
        }
        return result;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

然後我們在spring配置文件中配置切面

<!-- 配置切面的類 -->
<bean id="serviceAspect" class="com.lirui.StationCardServiceAspect"/>
<!-- 配置成註解方式尋找要被代理的對象 -->
<aop:aspectj-autoproxy/>
  • 1
  • 2
  • 3
  • 4

這樣,我們就可以統一處理異常和日誌了。


不足點

利用這種方式,只能打入參和出參,還有拋出異常時打異常日誌,不能打方法運行中的中間值,目前我只能想到,方法中間值的日誌,就是用原來的方式打出,不知道大家有沒有什麼好的方法。


spring aop的其他使用

推薦使用aspectJ來完成面向切面編程。我們還可以利用aop完成其他功能如記錄程序運行時間等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章