|
对于最新稳定版本,请使用 Spring Framework 7.0.6! |
操作已通知对象
无论你如何创建 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)、拦截器(interceptor)或其他类型的通知(advice)返回一个 Advisor。如果您添加的是一个 Advisor,那么在此索引位置返回的通知器就是您所添加的对象。如果您添加的是一个拦截器或其他类型的通知,Spring 会将其包装在一个通知器中,该通知器所包含的切入点(pointcut)始终返回 true。因此,如果您添加了一个 MethodInterceptor,那么在此索引位置返回的通知器就是一个 DefaultPointcutAdvisor,它会返回您提供的 MethodInterceptor 以及一个匹配所有类和方法的切入点。
addAdvisor() 方法可用于添加任意 Advisor。通常,持有切入点(pointcut)和通知(advice)的 Advisor 是通用的 DefaultPointcutAdvisor,你可以将其与任何通知或切入点一起使用(但不能用于引入(introductions))。
默认情况下,即使代理对象已经创建,仍然可以添加或移除通知器(advisor)或拦截器(interceptor)。唯一的限制是无法添加或移除引入型通知器(introduction 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)是否可取(此处无意双关),这一点值得商榷,尽管毫无疑问确实存在一些合理的使用场景。 然而,在开发阶段(例如在测试中),这种做法可能非常有用。我们有时发现,能够以拦截器或其他增强逻辑的形式添加测试代码,从而深入到我们想要测试的方法调用内部,是非常有用的。(例如,该增强逻辑可以进入为该方法创建的事务内部,或许执行 SQL 来验证数据库是否已被正确更新,然后再将事务标记为回滚。) |
根据创建代理的方式,通常可以设置一个 frozen 标志。在这种情况下,Advised 接口的 isFrozen() 方法将返回 true,此时任何试图通过添加或移除通知(advice)来修改代理的行为都会抛出 AopConfigException 异常。冻结被通知对象(advised object)状态的能力在某些场景下非常有用(例如,防止调用代码移除安全拦截器)。