Unified Exception Handling and Log Printing Using spring aop

  • 2021-09-16 07:07:38
  • OfStack

The code that we can easily write

It's easy to write code with a lot of try catch and logger. warn (), logger. error (), so that the original business logic of a method is only 5 lines, and with these, the code becomes 10 lines or more, such as:


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;
}

In fact, our business logic is only a few lines, but there are so many exception handling codes and log information codes in the middle.

How do I improve my code

We can use springaop to do a section, which is dedicated to logging and exception handling, so that we can reduce duplication of code.

The code is as follows:


@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;
}

We do exception handling and log in the section:


@Aspect
public class CardServiceAspect {
    private final Logger logger = LoggerFactory.getLogger("card");
    //  On-demand configuration of pointcut expressions 
    @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 + " Adj. " + methodName + " Executed ");
        Object[] args = joinPoint.getArgs();
        StringBuilder log = new StringBuilder(" Input parameter is ");
        for (Object arg : args) {
            log.append(arg + " ");
        }
        logger.warn(log.toString());
    }
    @AfterReturning(value = "execution(* *.*(..)))", returning = "returnVal")
    public void afterReturin(Object returnVal) {
        logger.warn(" The method ended normally , The return value of the method :" + returnVal);
    }
    @AfterThrowing(value = "StationCardServiceAspect.myPointcut()", throwing = "e")
    public void afterThrowing(Throwable e) {
        if (e instanceof StationErrorCodeException) {
            logger.error(" Exception found in notification StationErrorCodeException", e);
        } else {
            logger.error(" Unknown exception found in notification ", e);
        }
    }
    @Around(value = "StationCardServiceAspect.myPointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        logger.warn(" Pre-enhancement ...");
        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;
    }
}

We then configure the facets in the spring configuration file


<!--  Class for configuring facets  -->
<bean id="serviceAspect" class="com.lirui.StationCardServiceAspect"/>
<!--  Configured to annotate to find objects to be proxied  -->
<aop:aspectj-autoproxy/>

In this way, we can handle exceptions and logs in a unified way.

Deficiency point

In this way, you can only enter and exit parameters, and when you throw an exception, you can't play the intermediate value in the operation of the method. At present, I can only think that the log of the intermediate value of the method is played in the original way. I don't know if you have any good methods.

Other uses of spring aop

It is recommended to use aspectJ for aspect-oriented programming. We can also use aop to complete other functions such as recording the running time of programs.

aop Realizes Return Value Log of System 1 Record Request Method and System 1 Exception Handling

Receiving the request return value written to the log easy to check the problem needs, the first consideration is to use the interceptor, but the interceptor postHandle method can not get the return value.

Continue to look for new methods, find a convenient method by querying online once, and add the following configuration to log4j2.xml configuration file by using log4j2:


<AsyncLogger name="org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor" level="debug" additivity="false">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="allLog"/>
 </AsyncLogger>

In this way, the return value of the method can be recorded in the log, but the log recorded in this way is not convenient to view as well as other logs of the system. This method pass. Finally, this function can only be realized with spring aop, and the steps are as follows:

1. Introducing jar package dependent on aop


<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>4.0.0.RELEASE</version>
</dependency>
 
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.0.0.RELEASE</version>
</dependency>

2. Configure the xml file

Introducing the aop namespace


xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
<!--  Turn on the automatic section agent  -->
<aop:aspectj-autoproxy/>
<!--  Use annotation  Automatic registration bean -->
<context:component-scan base-package="com.zzz.dealer.**"/>

3. Write the section class


@Aspect  // Specifies that the current class is a facet class 
@Component // Put ordinary pojo Instantiate to spring Container, equivalent to the <bean id="" class=""/>
public class MethodLogAndExceptionAop {
 
    @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public Object around(ProceedingJoinPoint jp) throws Throwable {
        String targetName = jp.getTarget().getClass().getName();
        String methodName = jp.getSignature().getName();
        Object[] arguments = jp.getArgs();
        Object[] args = new Object[arguments.length];
        for (int i = 0; i < arguments.length; i++) {
            if (arguments[i] instanceof ServletRequest || arguments[i] instanceof ServletResponse || arguments[i] instanceof MultipartFile) { 
              //ServletRequest Can't serialize, exclude from the input parameter, otherwise report an exception: java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
              //ServletResponse Cannot serialize   Exclude from the entry parameter, otherwise report exception: java.lang.IllegalStateException: getOutputStream() has already been called for this response
                continue;
            }
            args[i] = arguments[i];
        }
        Object result = null;
        try {
            //StopWatch  Timing 
            StopWatch clock = new StopWatch();
            clock.start();
            result = jp.proceed();
            clock.stop();
            long executeTime = clock.getTime();
            LoggerUtil.info(targetName, methodName, " Call Controller Method returns the result ", result, executeTime, args);
        } catch (Exception exception) {
            LoggerUtil.error(targetName, methodName, " Unified 1 Exception handling ", exception, args);
            ResultVo resultVo = new ResultVo(false);
            //  To be on the safe side, only business exceptions are visible to the front end, otherwise, the system 1 Classified as a system exception 
            if (exception instanceof BusinessException) {
                resultVo.setResultAndCode(false, ((BusinessException) exception).getErrorCode(), ((BusinessException) exception).getErrorMessage());
            } else {
                resultVo.setResultAndCode(false, ErrorCode.DEALER_ERR_100000.getCode(), " System exception, please contact administrator ");
            }
            result = resultVo;
        }
        return result;
    }
}

The original exception handling of the system is to implement the custom exception handling of HandlerExceptionResolver interface. After implementing this aop, it is found that the system exception handling can also be implemented here, so the custom exception handling is killed. One lift two wins.


Related articles: