Manipulating Advised Objects

无论你如何创建 AOP 代理,你都可以使用 org.springframework.aop.framework.Advised 接口对其进行操作。无论它实现了哪些其他接口,任何 AOP 代理都可以被强制转换为该接口。此接口包括以下方法:

However you create AOP proxies, you can manipulate them BY using the org.springframework.aop.framework.Advised interface. Any AOP proxy can be cast to this interface, no matter which other interfaces it implements. This interface includes the following methods:

  • 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 和一个匹配所有类和方法的切入点。

The getAdvisors() method returns an Advisor for every advisor, interceptor, or other advice type that has been added to the factory. If you added an Advisor, the returned advisor at this index is the object that you added. If you added an interceptor or other advice type, Spring wrapped this in an advisor with a pointcut that always returns true. Thus, if you added a MethodInterceptor, the advisor returned for this index is a DefaultPointcutAdvisor that returns your MethodInterceptor and a pointcut that matches all classes and methods.

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

The addAdvisor() methods can be used to add any Advisor. Usually, the advisor holding pointcut and advice is the generic DefaultPointcutAdvisor, which you can use with any advice or pointcut (but not for introductions).

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

By default, it is possible to add or remove advisors or interceptors even once a proxy has been created. The only restriction is that it is impossible to add or remove an introduction advisor, as existing proxies from the factory do not show the interface change. (You can obtain a new proxy from the factory to avoid this problem.)

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

The following example shows casting an AOP proxy to the Advised interface and examining and manipulating its 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)

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

It is questionable whether it is advisable (no pun intended) to modify advice on a business object in production, although there are, no doubt, legitimate usage cases. However, it can be very useful in development (for example, in tests). We have sometimes found it very useful to be able to add test code in the form of an interceptor or other advice, getting inside a method invocation that we want to test. (For example, the advice can get inside a transaction created for that method, perhaps to run SQL to check that a database was correctly updated, before marking the transaction for roll back.)

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

Depending on how you created the proxy, you can usually set a frozen flag. In that case, the Advised isFrozen() method returns true, and any attempts to modify advice through addition or removal results in an AopConfigException. The ability to freeze the state of an advised object is useful in some cases (for example, to prevent calling code removing a security interceptor).