# Spring AOP

Aspect Oriented Programming（AOP）：意為：面向切面編程。從本質上講，它是一種無需修改代碼即可向現有代碼添加行為的方法。通過預編譯方式和運行期間動態代理實現程序功能的統一維護的一種技術。是Spring框架中的一個重要內容，利用AOP可以對業務邏輯的各個部分進行隔離，從而使得業務邏輯各部分之間的耦合度降低，提高程式的可重用性，同時提高了開發的效率。

主要功能\
日誌記錄、性能統計、安全控制、事務處理、異常處理等等。\
我們知道AOP是通過動態代理實現的，而Spring的AOP是實現有兩種，一個是JDK動態代理，一個是CGLIB實現。

## 技術詳解參考

* <https://www.baeldung.com/spring-aop>
* <https://bingdoal.github.io/backend/2020/11/aop-and-point-cut-in-spring-boot/>
* <https://blog.csdn.net/qq_36551991/article/details/106740138>

## 實作測試 - 效能統計(Method執行時間)

### annotation方式：

#### 定義一個annotation介面

```java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}
```

#### aspect class

```java
@Aspect
@Component
public class LogExecutionTimeAop {
	Logger log = LoggerFactory.getLogger("aopTest");

	@Pointcut("@annotation(com.ecommerce.mini.anno.LogExecutionTime)")
	public void logExecution() {
	}

	@Around("logExecution()")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
		log.debug("---> around");
		long start = System.currentTimeMillis();

		Object obj = joinPoint.proceed();

		long executionTime = System.currentTimeMillis() - start;
		log.debug("---> around " + joinPoint.getSignature() + " executed in " + executionTime + "ms");
		return obj;
	}

	@After("logExecution()")
	public void after(JoinPoint joinPoint) {
		log.debug("---> after");
	}

	@AfterReturning(value = "logExecution()", returning = "returnObject")
	public void afterReturning(JoinPoint joinPoint, Object returnObject) {
		log.debug("---> AfterReturning");
	}

	@Before("logExecution()")
	public void before(JoinPoint joinPoint) {
		log.debug("---> before");
	}
}
```

#### Controller

```java
@RestController
@RequestMapping("/common")
public class CommonController {
	Logger log = LoggerFactory.getLogger("aopTest");

	@GetMapping("/test")
	@LogExecutionTime
	public ResponseEntity test() {
		log.debug("do something.....");
		return ResponseEntity.ok();
	}
}

```

#### output

```
aopTest - ---> around
aopTest - ---> before
aopTest - do something.....
aopTest - ---> AfterReturning
aopTest - ---> after
aopTest - ---> around ResponseEntity com.ecommerce.mini.controller.CommonController.test(String) executed in 6ms
```

#### execution方式：

#### aspect class 增加

```java
@Pointcut("execution(* com.ecommerce.mini.controller.*Controller.*(..))")
public void pointcut() {
}
```

#### output

```
aopTest - ---> around
aopTest - ---> before
aopTest - do something.....
aopTest - ---> AfterReturning
aopTest - ---> after
aopTest - ---> around ResponseEntity com.ecommerce.mini.controller.CommonController.test(String) executed in 6ms
```

## 實作測試 - 日誌紀錄

實際應用場景會出現在記錄後台使用者CRUD記錄，當問題出現時可以根據當時User傳送的資料來當偵錯測試的依據。Server Reponse 有一定規範，操作上會更有架構性。

#### aspect class

```java
@AfterReturning(value = "pointcut()", returning = "returnObject")
public void afterReturning(JoinPoint joinPoint, Object returnObject) {
	log.debug("---> AfterReturning");
	try {
		ResponseEntity a = (ResponseEntity) returnObject;
		JSONResult b = (JSONResult) a.getBody();
		if (b.getUserActionLog() != null) {
			log.debug("afterReturning -> todo: insert user action log.");
		} else {
			log.debug("afterReturning -> not found user action log obj.");
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}
```

#### Controller

```java
@GetMapping("/test")
@LogExecutionTime
public ResponseEntity test() {
	log.debug("do something.....");
	UserActionLog actionLog = new UserActionLog();
	actionLog.setNewInfo("do something..... bla bla bla");
	return ResponseEntity.ok(JSONResult.createResult(SuccessCodeMsg.COMMON_OK).addUserActionLog(actionLog));
}
```

#### output

```
aopTest - ---> around
aopTest - ---> before
aopTest - do something.....
aopTest - ---> AfterReturning
aopTest - afterReturning -> todo: insert user action log.
aopTest - ---> after
aopTest - ---> around ResponseEntity com.ecommerce.mini.controller.CommonController.test() executed in 12ms
```

## 結語

在實際使用場景上，還是有許多需要考慮的地方，比如在比對前後異動資訊時，需要不去影響核心邏輯部分資料，在切分工作時要再更細心一點。

![這是我實際透過AOP 紀錄 user action log](/files/29YeMdAScX7lRaMBxPcZ)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xu-min-chang.gitbook.io/caster-develop-note/java/spring-boot/spring-aop.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
