程式狂想筆記

一個攻城師奮鬥史

0%

Spring Boot AOP 小記

之前我同事推坑 Spring Boot 框架給我,跟我說 Sping 重要觀念有兩個,一個是 注入 另一個是 AOP 。目前注入常常使用,但是不常使用 AOP,最近用點時間整理一下。

觀念

推薦

AspectJ报错:error at ::0 can’t find referenced pointcut XXX - 楼兰胡杨 - 博客园

菜鳥工程師 肉豬: Spring AOP get method advice annotation and parameters

4.3.3 处理通知中的参数 - Spring 实战(第四版)

AOP錯誤: error at ::0 can’t find referenced pointcut XXX

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
package com.example.demo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Component
@Aspect
public class TestAspect {
....

@Around("com.example.demo.TestClass.callMethod()")
public Object doAroundAccessCheck(ProceedingJoinPoint pjp) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 开始
Object retVal = pjp.proceed();
stopWatch.stop();
// 结束
System.out.println("invoke method: " + pjp.getSignature().getName() + ", elapsed time: " + stopWatch.getTotalTimeMillis());
return retVal;
}
}

可參考:AspectJ报错:error at ::0 can’t find referenced pointcut XXX - 楼兰胡杨 - 博客园

不過我參照上面方法都失敗,所以我後來嘗試加上execution,順利解決問題。

1
2
3
4
5
6
7
8
9
10
11
@Around("execution(* com.example.demo.TestClass.callMethod())")
public Object doAroundAccessCheck(ProceedingJoinPoint pjp) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 开始
Object retVal = pjp.proceed();
stopWatch.stop();
// 结束
System.out.println("invoke method: " + pjp.getSignature().getName() + ", elapsed time: " + stopWatch.getTotalTimeMillis());
return retVal;
}

名詞小記


框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习) - 小不点啊 - 博客园

解說名詞:

其實當初看敘述有看沒有懂,對照敘述我的理解先記錄下來,之後回頭複習希望能馬上進入狀況。

簡單我的觀點來看,Spring @Transactional都會用代理模式,這邊AOP其實也類似做一樣動作,底層(應該)用做代理模式去做,達成這個效果。

  • JoinPoint: 中文叫連接點,多個執行點可執行自訂要跑的程式(Advice)。
  • PointCut: 中文叫斷點,其中一個被選定的JoinPoint。
  • Advice: 就是你當PointCut 觸發要執行的程式。

程式配置可以參考:

記錄重點

pom.xml

不知道為什麼 Spring Initializr 找不到這個,要自己加到 pom.xml

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

程式

src/main/java/com/example/demo/DemoApplication.java

1
2
3
4
5
6
7
8
9
10
@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(DemoApplication.class, args);
var testClass = ctx.getBean(TestClass.class);
testClass.callMethod();
}

}

src/main/java/com/example/demo/TestClass.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class TestClass {

@Autowired
TestClass _testClass;

public void callMethod() {
System.out.println("Hello World!!!!你好世界");
_testClass.callMethod2();
}


public void callMethod2() {
System.out.println(" callMethod2");
}

}

src/main/java/com/example/demo/TestAspect.java

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
@Component
@Aspect
public class TestAspect {
@Before("execution(* callMethod())")
public void before(){
System.out.println("我在你前面做");
}

@After("execution(* callMethod())")
public void after(){
System.out.println("我在你後面做");
}

@Around("execution(* com.example.demo.TestClass.callMethod())")
public Object doAroundAccessCheck(ProceedingJoinPoint pjp) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 开始
Object retVal = pjp.proceed();
stopWatch.stop();
// 结束
System.out.println("invoke method: " + pjp.getSignature().getName() + ", elapsed time: " + stopWatch.getTotalTimeMillis());
return retVal;
}
}

其他重點

參考:Spring AOP中JoinPoint的用法 - 简书

Object[] getArgs(); 獲取傳入目標方法的引數物件
Object getTarget(); 獲取被代理的物件
Object getThis(); 獲取代理物件


參考:Spring AOP概念Aspect、Advice、JoinPoint、JoinCut與Execution_Anur的博客-CSDN博客_advice aspect


參考:4.3.3 处理通知中的参数 - Spring 实战(第四版)

感想

這邊源碼我有丟到 Github: malagege/spring-boot-aop-example: 範例測試

我以為很難,其實寫出範例又好像很簡單。後來想到滿多機制能用這個寫,像重試API測試,我們也可以透過AOP方式,後來我很好奇有沒有別人現成寫好的工具,發現spring-retry可以做到這些事情。

文章整理:

彩蛋

依賴注入循環問題。

1
spring.main.allow-circular-references=true

.Net Core 另外 Retry 方案