ITEEDU

ControlFlowPointcut

org.springframework.aop.support.ControlFlowPointcut 是Sping所提供的类别,作用为判断在方法的 呼叫堆叠中,某个指定类别的某方法中是否曾经要求您的目标物件执行某个动作,由于这是在执 行时期才会确定是 否介入Advices,所以是Spring提供的动态Pointcut功能。

以  NameMatchMethodPointcutAdvisor 中的 LogBeforeAdvice 类别 为例,您 想要知道 在 onlyfun.caterpillar.Some类别中,是否曾经有某个方法中要求过您指定 的目标物件执行某些动作, 如果有的话,则介入LogBeforeAdvice来提供日志服务以记录一些资讯,则您可以将Bean定义档如 下撰写:

•      beans‐config.xml
<?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 类别可以简单的如下撰写:

•      Some.java
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 的运作,如下所示:

•      SpringAOPDemo.java
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。