org.springframework.aop.support.ControlFlowPointcut 是Sping所提供的类别,作用为判断在方法的 呼叫堆叠中,某个指定类别的某方法中是否曾经要求您的目标物件执行某个动作,由于这是在执 行时期才会确定是 否介入Advices,所以是Spring提供的动态Pointcut功能。
以 NameMatchMethodPointcutAdvisor 中的 LogBeforeAdvice 类别 为例,您 想要知道 在 onlyfun.caterpillar.Some类别中,是否曾经有某个方法中要求过您指定 的目标物件执行某些动作, 如果有的话,则介入LogBeforeAdvice来提供日志服务以记录一些资讯,则您可以将Bean定义档如 下撰写:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN""http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="some" class="onlyfun.caterpillar.Some"/> <bean id="logBeforeAdvice" class="onlyfun.caterpillar.LogBeforeAdvice"/> <bean id="helloFlowControlPointcut" class="org.springframework.aop.support.ControlFlowPointcut"> <constructor-arg> <value>onlyfun.caterpillar.Some</value> </constructor-arg> </bean> <bean id="helloAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="advice"> <ref bean="logBeforeAdvice"/> </property> <property name="pointcut"> <ref bean="helloFlowControlPointcut"/> </property> </bean> <bean id="helloSpeaker" class="onlyfun.caterpillar.HelloSpeaker"/> <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>onlyfun.caterpillar.IHello</value> </property> <property name="target"> <ref bean="helloSpeaker"/> </property> <property name="interceptorNames"> <list> <value>helloAdvisor</value> </list> </property> </bean> </beans>
在 ControlFlowPointcut 建构时,指定了 onlyfun.caterpillar.Some 类别,表示若在 Some 类别中的某
个方法要求 了指定的目标物件(也就是 helloSpeaker 实例)执行某些动作,则应用 Before Advice (logBeforeAdvice)提供日志的服务,Some 类别可以简单的如下撰写:
package onlyfun.caterpillar; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class Some implements ApplicationContextAware { private IHello helloProxy; public void setApplicationContext( ApplicationContext context) throws BeansException { helloProxy = (IHello) context.getBean("helloProxy"); } public void helloEverybody() { helloProxy.helloNewbie("Justin"); helloProxy.helloMaster("caterpillar"); } }
为了方便您取得 ApplicationContext 以获得 helloSpeaker 的代理物件,Some 类别实现了 org.springframework.context.ApplicationContextAware 介面,接着可以撰写一个简单的程式来测试 一下 ControlFlowPointcut 的运作,如下所示:
package onlyfun.caterpillar; import org.springframework.context.ApplicationContext; import org.springframework.context. support.FileSystemXmlApplicationContext; public class SpringAOPDemo { public static void main(String[] args) { ApplicationContext context = new FileSystemXmlApplicationContext( "beans-config.xml"); Some some = (Some) context.getBean("some"); if(args.length > 0 && "run".equals(args[0])) { some.helloEverybody(); } else { System.out.println("作其它的事情..."); } } }
其余未列出的程式,都与 NameMatchMethodPointcutAdvisor 中的程式相同,如果您执行程式 时没有提供"run"引数,则会出现"作其它的事情"的文字讯息,这是因为没有执行Some实例的 helloEverybody(),在呼叫堆叠中并不符合所指定的:ome类别的某方法曾要求helloSpeaker实例执 行某些动作。
如果您执行程式时提供了"run"引数,则会呼叫Some类别的helloEverybody()方法,方法中要求 helloSpeaker的代理物件执 行helloNewbie()与helloMaster()方法,符合Bean定义档中指定的内容, 因而会应用LogBeforeAdvice来提供服务 讯息。
动态Pointcut的问题就是在于效能上的付出,由于呼叫堆叠的判断是在执行时期进行,所以执行 时会很慢,在不同的JDK上可能会有 5 到 10 倍的效能延缓,因此建议在可能的情况下,尽量使用 静态Pointcut。