Spring AOP 介紹與簡單DEMO
通知(Advice)
Spring中的切面一共提供5種通知的類型:
前置通知(@Before)
後置通知(@After)
返回通知(@AfterReturning)
異常通知(@AfterThrowing)
環繞通知(@Around)
前面4個較爲容易理解,例如“前置通知”,我們通常在一個方法的第一句打印出傳入的方法參數,此時就可以使用前置通知在方法調用前打印出傳入的參數。對於“後置通知”實際是“返回通知”和“異常通知”的並集,返回通知表示程序正確運行返回後執行,異常通知表示程序不正常運行拋出異常時執行,而後置通知則不論程序是否正確運行,一旦離開方法就會執行。
環繞通知最爲強大,它包裹了被通知的方法,可同時定義前置通知和後置通知。
切點(Pointcut)
通知定義了何時工作以及工作內容,切點則定義了在何處工作,也就是在哪個方法應用通知。要表達出在哪個方法中運用通知,這需要用到切點表達式。Spring AOP藉助AspectJ(另一種AOP實現)的切點表達式來確定通知被應用的位置,雖然是藉助但並不支持所有AspectJ的所有切點指示器而僅僅是其一個子集,這其中最爲常用的就是execution切點指示器,表示執行。例如:
execution(* com.deo.springaop.Test.test(..))
DEMO
1. 引用依賴
1) 需要引用的AOP依賴:
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
2) 需要引用的測試依賴:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.7.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
2. Spring 配置自動掃描
3. 定義一個業務類DEMO:
package aop.service;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSONObject;
@Component
public class AuthService {
public boolean delete(JSONObject s) {
System.out.println("-- delete : " + s);
return false;
}
}
4. 針對此業務類定義一個切面:
@Aspect
@Component
public class AopUtil {
@AfterReturning(returning = "res", pointcut = "execution(* aop.service.*.delete(..))")
public void check(JoinPoint joinPoint, boolean res) {
System.out.println("-- check -- param: " + joinPoint.getArgs() + " -- res: " + res);
Object objArr[] = joinPoint.getArgs();
for(Object obj : objArr) {
System.out.println("-- param: " + obj.toString());
}
}
}
也可以寫成:
@Aspect
@Component
public class AopUtil {
@Pointcut("execution(* aop.service.*.delete(..))")
public void pointcut() {
System.out.println("-- pointcut --");
}
@AfterReturning(value = "pointcut()", returning = "res")
public void check(JoinPoint joinPoint, boolean res) {
System.out.println("-- check -- param: " + joinPoint.getArgs() + " -- res: " + res);
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object objArr[] = joinPoint.getArgs();
for(Object obj : objArr) {
System.out.println("-- param: " + obj.toString());
}
}
}
@AfterReturning:定義在方法返回時執行此切面。
@Pointcut:定義了一個切點,execution 標識在哪些方法會觸發此切點。
returning:可以獲取方法返回的參數.
JoinPoint joinPoint: 通過此對象可以獲取方法的信息以及入參參數。
5. 測試類:
package aop.test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import com.alibaba.fastjson.JSONObject
import aop.service.AuthService;
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = {"classpath*:applicationContext.xml"})
public class Test {
@Autowired
private AuthService auth;
@org.junit.Test
public void delete() {
JSONObject json = new JSONObject();
json.put("name", "張三");
json.put("age", "17");
auth.delete(json);
}
}
6. 運行結果:
-- delete : {"age":"17","name":"張三"}
-- check -- param: [Ljava.lang.Object;@76b1e9b8 -- res: false
-- param: {"age":"17","name":"張三"}
結果表明在執行業務類的delete方法之後,執行了切面的check方法。
AOP編程可以運用在日誌、事務、權限控制等等一些地方,只要你想。