Springboot整合AOP和註解,實現豐富的切面功能

2023-02-08 12:01:21

簡介

我們在文章《Spring AOP與AspectJ的對比及應用》介紹了AOP的使用,這篇文章講解一下AOP與註解的整合,通過註解來使用AOP,會非常方便。為了簡便,我們還是來實現一個計時的功能。

整合過程

首先建立一個註解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PkslowLogTime {
}

然後在一個Service中使用註解:

@Service
@Slf4j
public class TestService {
    @PkslowLogTime
    public void fetchData() {
        log.info("fetchData");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

這個Service的方法會在Controller中呼叫:

@GetMapping("/hello")
public String hello() {
  log.info("------hello() start---");
  test();
  staticTest();
  testService.fetchData();
  log.info("------hello() end---");
  return "Hello, pkslow.";
}

接著是關鍵一步,我們要實現切面,來找到註解並實現對應功能:

@Aspect
@Component
@Slf4j
public class PkslowLogTimeAspect {
    @Around("@annotation(com.pkslow.springboot.aop.PkslowLogTime) && execution(* *(..))")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("------PkslowLogTime doAround start------");
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        // Get intercepted method details
        String className = methodSignature.getDeclaringType().getSimpleName();
        String methodName = methodSignature.getName();

        // Measure method execution time
        StopWatch stopWatch = new StopWatch(className + "->" + methodName);
        stopWatch.start(methodName);
        Object result = joinPoint.proceed();
        stopWatch.stop();
        // Log method execution time
        log.info(stopWatch.prettyPrint());
        log.info("------PkslowLogTime doAround end------");
        return result;
    }
}

@Around("@annotation(com.pkslow.springboot.aop.PkslowLogTime) && execution(* *(..))")這個表示式很關鍵,如果不對,將無法正確識別;還有可能出現多次呼叫的情況。多次呼叫的情況可以參考:Stackoverflow

這裡使用了Spring的StopWatch來計時。

測試

通過maven build包:

$ mvn clean package

紀錄檔可以看到有對應的織入資訊:

[INFO] Join point 'method-execution(java.lang.String com.pkslow.springboot.controller.TestController.hello())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:22) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(java.lang.String com.pkslow.springboot.controller.TestController.hello())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:22) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.test())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:31) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.test())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:31) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.staticTest())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:37) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.staticTest())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:37) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.service.TestService.fetchData())' in Type 'com.pkslow.springboot.service.TestService' (TestService.java:12) advised by around advice from 'com.pkslow.springboot.aop.PkslowLogTimeAspect' (PkslowLogTimeAspect.class(from PkslowLogTimeAspect.java))

啟動應用後存取介面,紀錄檔如下:

總結

通過註解可以實現很多功能,也非常方便。而且註解還可以新增引數,組合使用更完美了。

程式碼請看GitHub: https://github.com/LarryDpk/pkslow-samples