天天看點

Spring Aspect程式設計中@Around注解的使用及接口Controller監控

Spring Aspect程式設計中,Spring切面包含通知和切點,通知和切點分别定義了在何時何處執行切面邏輯。

其中,Spring定義了五種不同類型的通知:

  • Before(目标方法執行前)
  • After(目标方法執行後,不關注執行結果)
  • After-returning(目标方法執行後,傳回通知)
  • After-throwing(目标方法抛出異常後)
  • Around(目标方法執行前後、異常)

Spring切點,通過比對規則查找合适的連接配接點,AOP會在這些連接配接點上織入通知

本文僅關注@Around,通過切面邏輯并在其中添加Log或其他方式,幫助我們快速定位異常點,友善系統監控和bug排查,并通過下方代碼展示其使用方式

@Aspect
@Component
@Slf4j
public class ControllerLogAop {

    @Pointcut("execution(*  *..*.*.controller..*.*(..))")
    public void controller() {
    }

    @Around("controller()")
    public Object controller(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 擷取類名
        String className = proceedingJoinPoint.getTarget().getClass().getSimpleName();
        // 擷取方法名
        String methodName = proceedingJoinPoint.getSignature().getName();
        // 擷取方法的參數
        Object[] args = proceedingJoinPoint.getArgs();

        // 擷取controller的請求屬性資料
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(requestAttributes).getRequest();

        printRequestLog(request, className, methodName, args);
        long start = System.currentTimeMillis();
        // 繼續執行方法邏輯
        Object returnObj = proceedingJoinPoint.proceed();
        printResponseLog(request, className, methodName, returnObj, System.currentTimeMillis() - start);
        return returnObj;
    }


    private void printRequestLog(HttpServletRequest request, String clazzName, String methodName, Object[] args) throws JsonProcessingException {
        log.debug("Request URL: [{}], URI: [{}], Request Method: [{}], IP: [{}]",
                request.getRequestURL(),
                request.getRequestURI(),
                request.getMethod(),
                ServletUtil.getClientIP(request));

        if (args == null || !log.isDebugEnabled()) {
            return;
        }

        boolean shouldNotLog = false;
        for (Object arg : args) {
            if (arg == null ||
                    arg instanceof HttpServletRequest ||
                    arg instanceof HttpServletResponse ||
                    arg instanceof MultipartFile ||
                    arg.getClass().isAssignableFrom(MultipartFile[].class)) {
                shouldNotLog = true;
                break;
            }
        }

        if (!shouldNotLog) {
            String requestBody = JsonUtils.objectToJson(args);
            log.debug("{}.{} Parameters: [{}]", clazzName, methodName, requestBody);
        }
    }

    private void printResponseLog(HttpServletRequest request, String className, String methodName, Object returnObj, long usage) throws JsonProcessingException {
        if (log.isDebugEnabled()) {
            String returningData = null;
            if (returnObj != null) {
                if (returnObj.getClass().isAssignableFrom(byte[].class)) {
                    returningData = "Binary data";
                } else {
                    returningData = JsonUtils.objectToJson(returnObj);
                }
            }
            log.debug("{}.{} Response: [{}], usage: [{}]ms", className, methodName, returningData, usage);
        }
    }
}
           

參考文獻:

代碼學習來源