操控被通知对象
无论你如何创建 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 中,该 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
。冻结被通知对象状态的能力在某些情况下很有用(例如,防止调用代码删除安全拦截器)。