Manipulating Advised Objects

无论你如何创建 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、拦截器或其他建议类型返回一个 Advisor。如果你添加了一个 Advisor,该索引处返回的 Advisor 就是你添加的对象。如果你添加了一个拦截器或其他建议类型,Spring 会将它包装到一个 Advisor 中,并提供始终返回 true 的切入点。因此,如果你添加了一个 MethodInterceptor,此索引返回的 Advisor 就是一个 DefaultPointcutAdvisor,它会返回你的 MethodInterceptor 和一个匹配所有类和方法的切入点。

可以使用 addAdvisor() 方法添加任何 Advisor。通常情况下,持有切入点和建议的 Advisor 是通用的 DefaultPointcutAdvisor,你可以将其与任何建议或切入点结合使用(但不适用于引入)。

默认情况下,即使在一个代理被创建之后,也可以添加或删除 Advisor 或拦截器。唯一的限制是无法添加或删除引入 Advisor,因为工厂中现有的代理不会显示接口变更。(你可以从工厂获取一个新的代理以避免此问题。)

以下示例展示了将一个 AOP 代理强制转换为 Advised 接口以及检查和操作其建议:

  • 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)

在生产环境中修改业务对象建议是否明智(无意冒犯),这是值得商榷的,尽管毫无疑问有合法的使用案例。但是,它在开发中(例如在测试中)会非常有用。我们有时发现,能够以拦截器或其他建议的形式添加测试代码非常有用,进入我们想要测试的方法调用。(例如,建议可能会进入为此方法创建的事务中,也许可以运行 SQL 以检查数据库是否正确更新,然后标记事务回滚。)

根据你创建代理的方式,你通常可以设置一个 frozen 标记。在这种情况下,Advised isFrozen() 方法返回 true,并且通过添加或删除尝试修改建议将导致 AopConfigException。在某些情况下,冻结受建议对象的 state 很有用(例如,防止调用代码移除安全拦截器)。