Spring AOP & 切面表達式


SpringAOP 和 AspectJ 的關系:
它們是兩種不同的編程風格, SpringAOP 使用 xml 配置的形式配置 aop。而 AspectJ 使用 AspectJ 的注解來配置 aop

aspect、JoinPoint、Pointcut、Weaving、Advice
JoinPoint: 連接點。表示目標對象中的方法
Pointcut: 切點。表示連接點的集合
Weaving: 織入。把代理邏輯加入到目標對象上的過程叫織入
Advice: 通知。包括 “around”, “before” and “after 等


this、target
this: 產生的代理對象
target: 被代理的原始對象

JoinPoint、ProceedingJoinPoint
JoinPoint 接口可以拿到連接點的相關信息,比如:方法簽名、方法參數、this、target
ProceedingJoinPoint 繼承自 JoinPoint,它是用來支持環繞(around)通知的,多暴露了一個 proceed() 方法,用來執行目標對象的方法。

 

切面表達式:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-pointcuts-examples

execution

除了 ret-type-pattern, name-pattern, param-pattern 以外,其他的部分都是可選的(? 表示可選項)。

* 最常用作返回類型格式(ret-type-pattern),它表示匹配任何返回類型。
* 也可以用作名稱格式(name-pattern)的全部或一部分。
參數格式(param-pattern)稍微復雜一些:
()   : 匹配不帶參數的方法
(..) : 匹配任意數量(零個或多個)的參數
(*)  : 匹配任何類型的一個參數的方法
(*,String) : 匹配兩個參數的方法,第一個可以是任何類型,而第二個必須是字符串

更多語法可以參考 AspectJ 的文檔:https://www.eclipse.org/aspectj/doc/released/progguide/semantics-pointcuts.html

 

execution 與 within 的區別

execution 與 within 的區別:粒度不一樣 execution 的最小粒度可以控制到方法級別; within 的最小粒度只能控制到類級別;

例子:

execution(public * *(..))  // 匹配任意公共方法
execution(* set*(..))      // 匹配任意名稱為 set 開頭的方法
execution(* com.xyz.service.AccountService.*(..))    // 匹配 AccountService 類中的任意方法
execution(* com.xyz.service.*.*(..))    // 匹配 com.xyz.service 包下的任意類的任意方法
execution(* com.xyz.service..*.*(..))    // 匹配 com.xyz.service 包及子包下的任意類的任意方法

within(com.xyz.service.*)    // 匹配 com.xyz.service 包下的任意類的任意方法
within(com.xyz.service..*)   // 匹配 com.xyz.service 包及子包下的任意類的任意方法

 

this 和 target 的區別

this 匹配的是代理對象,即從 SpringIOC 容器中拿出來的代理對象;
target 匹配的是目標對象,即被代理的原始對象。
this(com.xyz.service.AccountService) // 匹配代理對象的所有方法,且代理對象實現了 AccountService
target(com.xyz.service.AccountService) // 匹配目標對象,且目標對象實現了 AccountService
注意: 使用 this 和 target 時,要注意代理實現是 JDK 還是 Cglib,兩者的表現不同。

  1. 使用 JDK 動態代理產生的代理類(this)是繼承了 java.lang.reflect.Proxy 后再實現了被代理的接口的,產生的代理類的代碼里面會去調用目標類(target),再結合 InvocationHandler 來產生代理效果。

  2. 使用 Cglib 動態代理產生的代理類(this)是直接繼承了被代理的目標類的(target)。

例子:

public interface AccountService {...}
public class AccountServiceImpl {...}
開啟 AOP 的方式: @EnableAspectJAutoProxy(proxyTargetClass=false)  // 默認走 JDK 的動態代理


public class Test {
@Autowired
AccountService accountService;
publict test1(){
 // 通過自動注入的方法獲取到 AccountService,來調用它里面的方法
 accountService.m1();
}

publict test2(){
 // 通過從 applicationContext 中獲取 AccountService 的 bean,來調用它里面的方法
 AccountService accountService2 = applicationContext.getBean(AccountService.class);
 accountService2.m1();
}
}

 

  • @PointCut("this(com.xyz.service.AccountServiceImpl)") : 它表示匹配代理對象的所有方法,且代理對象是 AccountServiceImpl 的實例,即 "代理對象 instanceof AccountServiceImpl == true"。 如果 AccountService 是代理 bean 的話,也會是 JDK 動態代理產生的代理 bean,那么它的 proxyClass 是一個繼承了 java.lang.reflect.Proxy 並且實現了 AccountService 接口的一個代理類,類似於 public class $proxy extend Proxy implements AccountService {},所以,這個代理類不是 AccountServiceImpl 的實例。故這個切面不起作用。

  • @PointCut("target(com.xyz.service.AccountServiceImpl)") : 它表示匹配目標對象的所有方法,且目標對象是 AccountServiceImpl 的實例。顯然,在 Test 類中目標對象就是 AccountServiceImpl,所以切面會起作用。

  • 將代理改為默認使用 Cglib : @EnableAspectJAutoProxy(proxyTargetClass=true) 由於 Cglib 動態代理是采用繼承目標類的方式來實現的,所以,@PointCut("this(com.xyz.service.AccountServiceImpl)") 與 @PointCut("target(com.xyz.service.AccountServiceImpl)") 兩個切面都會生效。

 

args

匹配指定參數類型和參數數據的方法。

args(java.io.Serializable)   // 匹配只有一個參數的方法,且參數類型為 Serializable

通過這個表達式還可以為切面通知傳參,具體參考:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-ataspectj-advice-params-generics

 

@target、@within、@annotation、@args

這些都是與注解一起使用的。

@target(org.springframework.transaction.annotation.Transactional)   // 匹配目標對象上有 @Transactional
@within(org.springframework.transaction.annotation.Transactional)   // 與上面類似
@annotation(org.springframework.transaction.annotation.Transactional)  // 匹配方法上面帶有 @Transactional
@args(com.xyz.security.Classified)    // 匹配只有一個參數的方法,且參數上有 @Classified  

注意:@annotation 只能用來匹配方法,而 @target、@within 可以用來匹配類。(粒度不同)

 

bean

 bean(tradeService)    // 匹配 bean 的名字叫 tradeService 的類
bean(*Service)    // 匹配 bean 的名字以 Service 結尾的

 

通過 && 、||、! 來組合

上面所有的表達式都可以通過 && 、||、! 來組合使用。

 

處理泛型&給 Advice 傳參

參考: https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-ataspectj-advice-params-generics

public interface Sample<T> {
   void sampleGenericMethod(T param);
   void sampleGenericCollectionMethod(Collection<T> param);
}

@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
public void beforeSampleMethod(Collection<MyType> param) {
   // Advice implementation
}

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM