在 Spring Boot 使用 AOP 印日誌
使用 AOP (Aspect Oriented Programming) 的方式印出日誌,會比在各處程式中寫印日誌來的簡潔,集中管理印日誌的程式,避免影響閱讀業務邏輯。
建置 Log4j2
參考這篇建置 Log4j2 文章。
加入 Dependency
在 build.gradle
加入 Spring Boot AOP dependencies:spring-boot-starter-aop
,
1 2 3 4 5 6 7 8
| dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-aop' compileOnly 'org.projectlombok:lombok:1.18.18' annotationProcessor 'org.projectlombok:lombok:1.18.18'
testImplementation 'org.springframework.boot:spring-boot-starter-test' }
|
定義 TimeLogAspect
想像各個業務邏輯是縱向的流程,而 AOP 就是將流程橫剖後織入程式,藉此達到關注點分離。
在元件上標註 @Aspect
,即可定義為一個切面,注意也要將物件加註 @Component
,Spring Boot 框架才得以管理這個元件。在方法上標註 @Around
指的是在切面的前、後織入程式,參數 ProceedingJoinPoint
是相對於橫切面的縱向資料流,可以由此參數取得資料流中的方法簽章和傳入參數等資訊。而 @Pointcut
定義切面的切點,例如,切點可以是有標註自定義的 Annotation,或是某個 Controller Package 下的所有 method。
以下是定義在所有標註 @TimeLog
或 Controller 的 Aspect 範例,計算執行這些方法需要多少時間,並將執行時間於方法回傳後印到日誌中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Log4j2 @Aspect @Component public class TimeLogAspect {
@Around("logTime() || controller()") public Object logAround(ProceedingJoinPoint pjp) throws Throwable { long startMillis = System.currentTimeMillis(); Object proceed = pjp.proceed(); long executionTime = System.currentTimeMillis() - startMillis; log.info(String.format("Completed %s in %d ms", pjp.getSignature().toShortString(), executionTime)); return proceed; }
@Pointcut("@annotation(cc.secondbrain.demo.annotation.TimeLog)") public void logTime() { }
@Pointcut("execution(* cc.secondbrain.demo.controller.*.*(..))") public void controller() { }
}
|
自定義 Annotation @TimeLog
如下,
1 2 3 4
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface TimeLog { }
|
使用自定義 Annotation @TimeLog
,可以自由地在想要記錄執行時間的方法上註記,但只限於 public
方法。
值得特別注意的是,假如在 Controller 註記 @TimeLog
,只會印出一行執行時間的日誌,不會重複印成兩行。
1 2 3 4 5 6 7 8 9 10
| @RestController public class HelloController {
@TimeLog @RequestMapping("/") public String hello() { return "Hello Spring Boot"; }
}
|