6.7. 操作切面对象

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

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();

getAdvisors()方法为已添加到工厂的每个Advisor、拦截器或其他切面类型返回一个Advisor。如果您添加了Advisor,则在此索引处返回的Advisor就是您添加的对象。如果您添加了拦截器或其他通知类型,Spring 会将其包装在一个Advisor中,并带有一个始终返回的切入点true。因此,如果您添加了 aMethodInterceptor,则为该索引返回的Advisor是 DefaultPointcutAdvisor,它返回您的 MethodInterceptor和匹配所有类和方法的切入点。

这些addAdvisor()方法可用于添加任何Advisor. 通常,持有切入点和切面的Advisor是通用DefaultPointcutAdvisor的,您可以将其与任何切面或切入点一起使用(但不能用于介绍)。

默认情况下,即使已创建代理,也可以添加或删除Advisor或拦截器。唯一的限制是不可能添加或删除介绍Advisor,因为工厂的现有代理不显示界面更改。(您可以从工厂获得一个新的代理来避免这个问题。)

以下示例显示了将 AOP 代理强制转换为Advised接口并检查和操作其切面:

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

在生产中修改关于业务对象的切面是否可取(不是双关语)是值得怀疑的,尽管毫无疑问,有合法的使用案例。但是,它在开发中非常有用(例如,在测试中)。我们有时发现能够以拦截器或其他切面的形式添加测试代码非常有用,进入我们想要测试的方法调用。(例如,通知可以进入为该方法创建的事务中,可能在将事务标记为回滚之前运行 SQL 以检查数据库是否正确更新。)

根据您创建代理的方式,您通常可以设置一个frozen标志。在这种情况下,该Advised isFrozen()方法返回true,并且任何通过添加或删除来修改切面的尝试都会导致AopConfigException. 冻结切面对象状态的能力在某些情况下很有用(例如,防止调用代码删除安全拦截器)。

最后更新于