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,首先要定义目标物件必须实现的介面:
package onlyfun.caterpillar; public interface IHello { public void hello(String name); }
接着定义一个 HelloSpeaker 类别,让其实现 IHello 介面:
package onlyfun.caterpillar; public class HelloSpeaker implements IHello { public void hello(String name) { System.out.println("Hello, " + name); } }
现在 HelloSpeaker 已经撰写完毕,在不对它进行任何修改的情况下,您想要在 hello()方法执 行之前,可以记录一些讯息,想像一下这是您拿到 的一个组件,您没有原始码,但您想对它增 加一些日志的服务。您可以先实作 MethodBeforeAdvice 介面,例如:
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 类别被设计为一 个独立的服务,可以重复提供服务给需要的物件,接着您只要在定义档中如下定义:
<?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 重视物件 的辨识,设计可重复使用的物件。