之前我同事推坑 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 方案