ITEEDU

Before Advice

Before Advice 会在目标物件的方法执行之前被呼叫,您可以实现 org.springframework.aop.MethodBeforeAdvice 介面来实作 Before Advice 的逻辑,该介面于 Spring 中的定义如下所示:

package org.springframework.aop;
public interface MethodBeforeAdvice extends BeforeAdvice {
	void before(Method method, Object[] args, Object target) throws Throwable;
}

在定义中可以看到,MethodBeforeAdvice 继承自 BeforeAdvice 介面,而 BeforeAdvice 介面又 继承自 Advice 介 面,后两者都是标签介面(Tag interface),只是用作标示而无定义任何方 法,MethodBeforeAdvice 继承了 BeforeAdvice,before()方法会在 目标物件(Target)上指定 的方法执行之前被呼叫,您可以取得被执行的 Method 实例、引数及目标物件,before()方法上 宣告为 void,所 以不传回任何的结果,在 before()方法执行完毕之后,除非您丢出例外,否则 目标物件上的方法就会被执行。

以实例来示范如何使用 Before Advice,首先要定义目标物件必须实现的介面:

•      IHello.java
package onlyfun.caterpillar;
public interface IHello {
	public void hello(String name);
}

接着定义一个 HelloSpeaker 类别,让其实现 IHello 介面:

•      HelloSpeaker.java
package onlyfun.caterpillar;
public class HelloSpeaker implements IHello {
	public void hello(String name) { System.out.println("Hello, " + name);
	}
}

现在 HelloSpeaker 已经撰写完毕,在不对它进行任何修改的情况下,您想要在 hello()方法执 行之前,可以记录一些讯息,想像一下这是您拿到 的一个组件,您没有原始码,但您想对它增 加一些日志的服务。您可以先实作 MethodBeforeAdvice 介面,例如:

•      LogBeforeAdvice.java
package onlyfun.caterpillar;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.aop.MethodBeforeAdvice;
public class LogBeforeAdvice
implements MethodBeforeAdvice {
	private Logger logger = Logger.getLogger(this.getClass().getName());
	public void before(Method method, Object[] args, Object target) throws Throwable {
		logger.log(Level.INFO,	"method starts..." + method);
	}
}

在 before()方法的实作中,您加入了一些记录资讯的程式码,LogBeforeAdvice 类别被设计为一 个独立的服务,可以重复提供服务给需要的物件,接着您只要在定义档中如下定义:

•      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="logBeforeAdvice" class="onlyfun.caterpillar.LogBeforeAdvice"/>
	<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>logBeforeAdvice</value>
			</list>
		</property>
	</bean>
</beans>

注意到除了建立 Advice 及 Target 的物件实例之外,您还使用了 org.springframework.aop.framework.ProxyFactoryBean,这个类别会被 BeanFactory 或是 ApplicationContext 用来建立代理物件(回忆一下前一个小节,Spring AOP 主要是透过代理机 制来实现,因而需要建立代理物件),您要在"proxyInterfaces"属性上告知代理时的可运用的 介面,在 "target"上告知 Target 物件,在"interceptorNames"上告知所要应用的 Advice 实例, 在不指定目标方法时,Before Advice 会被缝合(Weave)至介面上所有定义的方法之前。

可以撰写以下的程式测试一下 Before Advice 的运作:

•      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");
		IHello helloProxy =(IHello) context.getBean("helloProxy");
		helloProxy.hello("Justin");
	}
}

记得在操作取回的代理物件时,必须转换操作介面为 IHello 介面,执行结果将会在呼叫 hello() 方法前进行日志动作。

您所设计的 HelloSpeaker 与 LogBeforeAdvice 是两个独立的物件,对于 HelloSpeaker 来说,它 不用知道 LogBeforeAdvice 的存在(也就是没有任何与 LogBeforeAdvice 相关的 API 撰写在 HelloSpeaker 中),而 LogBeforeAdvice 也可以运用至其它的物件之上,HelloSpeaker 与 LogBeforeAdvice 都是可以重复使用的设计。

可以看出 AOP 的精神,着重于 Aspects 的辨识,设计可重复使用的 Advices,就如 OOP 重视物件 的辨识,设计可重复使用的物件。