操作 Advised 物件

無論您如何建立 AOP 代理,您都可以透過使用 org.springframework.aop.framework.Advised 介面來操作它們。任何 AOP 代理都可以轉換為此介面,無論它實作了哪些其他介面。此介面包含以下方法

  • Java

  • Kotlin

Advisor[] getAdvisors();

void addAdvice(Advice advice) throws AopConfigException;

void addAdvice(int pos, Advice advice) throws AopConfigException;

void addAdvisor(Advisor advisor) throws AopConfigException;

void addAdvisor(int pos, Advisor advisor) throws AopConfigException;

int indexOf(Advisor advisor);

boolean removeAdvisor(Advisor advisor) throws AopConfigException;

void removeAdvisor(int index) throws AopConfigException;

boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;

boolean isFrozen();
fun getAdvisors(): Array<Advisor>

@Throws(AopConfigException::class)
fun addAdvice(advice: Advice)

@Throws(AopConfigException::class)
fun addAdvice(pos: Int, advice: Advice)

@Throws(AopConfigException::class)
fun addAdvisor(advisor: Advisor)

@Throws(AopConfigException::class)
fun addAdvisor(pos: Int, advisor: Advisor)

fun indexOf(advisor: Advisor): Int

@Throws(AopConfigException::class)
fun removeAdvisor(advisor: Advisor): Boolean

@Throws(AopConfigException::class)
fun removeAdvisor(index: Int)

@Throws(AopConfigException::class)
fun replaceAdvisor(a: Advisor, b: Advisor): Boolean

fun isFrozen(): Boolean

getAdvisors() 方法會為已新增至工廠的每個 advisor、攔截器或其他 advice 類型傳回一個 Advisor。如果您新增了 Advisor,則此索引處傳回的 advisor 是您新增的物件。如果您新增了攔截器或其他 advice 類型,Spring 會將其包裝在一個 advisor 中,該 advisor 的切入點始終傳回 true。因此,如果您新增了 MethodInterceptor,則為此索引傳回的 advisor 是一個 DefaultPointcutAdvisor,它會傳回您的 MethodInterceptor 和一個符合所有類別和方法的切入點。

addAdvisor() 方法可用於新增任何 Advisor。通常,持有切入點和 advice 的 advisor 是通用的 DefaultPointcutAdvisor,您可以將其與任何 advice 或切入點一起使用(但不適用於引介)。

預設情況下,即使在建立代理後,也可以新增或移除 advisor 或攔截器。唯一的限制是無法新增或移除引介 advisor,因為工廠中現有的代理不會顯示介面變更。(您可以從工廠取得新的代理以避免此問題。)

以下範例顯示將 AOP 代理轉換為 Advised 介面,並檢查和操作其 advice

  • Java

  • Kotlin

Advised advised = (Advised) myObject;
Advisor[] advisors = advised.getAdvisors();
int oldAdvisorCount = advisors.length;
System.out.println(oldAdvisorCount + " advisors");

// Add an advice like an interceptor without a pointcut
// Will match all proxied methods
// Can use for interceptors, before, after returning or throws advice
advised.addAdvice(new DebugInterceptor());

// Add selective advice using a pointcut
advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice));

assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length);
val advised = myObject as Advised
val advisors = advised.advisors
val oldAdvisorCount = advisors.size
println("$oldAdvisorCount advisors")

// Add an advice like an interceptor without a pointcut
// Will match all proxied methods
// Can use for interceptors, before, after returning or throws advice
advised.addAdvice(DebugInterceptor())

// Add selective advice using a pointcut
advised.addAdvisor(DefaultPointcutAdvisor(mySpecialPointcut, myAdvice))

assertEquals("Added two advisors", oldAdvisorCount + 2, advised.advisors.size)
在生產環境中修改業務物件上的 advice 是否明智(絕非雙關語)是值得懷疑的,儘管毫無疑問存在合法的使用案例。但是,它在開發中可能非常有用(例如,在測試中)。我們有時發現能夠以攔截器或其他 advice 的形式新增測試程式碼非常有用,以便進入我們要測試的方法調用中。(例如,advice 可以進入為該方法建立的交易中,可能執行 SQL 以檢查資料庫是否已正確更新,然後將交易標記為回滾。)

根據您建立代理的方式,您通常可以設定 frozen 標誌。在這種情況下,Advised isFrozen() 方法會傳回 true,並且任何嘗試透過新增或移除來修改 advice 的操作都會導致 AopConfigException。在某些情況下,凍結 advised 物件狀態的能力非常有用(例如,防止呼叫程式碼移除安全性攔截器)。