Java Dynamic Proxy 可以对类的指定方法实现拦截,但要求此类要实现某个Interface,拦截的方法出自此Interfacle。基于此,我们可以实现简单的AOP,要完成方法的拦截,只需要一个InvocationHandler,这个InvocationHandler即是拦截逻辑所在,实现了对目标object指定方法的筛选及调用。
比如我们现有一对Interface/Implemention:Speakable/Translater。
Speakable接口指明了可以sayEnglish(),sayChinese()。我们目前的translater实现了这两个方法。现在我们在此基础上,增加对translater的方法的logging功能。
在以前,我们可以:
1.直接改写原代码,增加logging功能。(ugly)
2.增加一个LoggingTranlater实现Speakable,采用delegation(Design Pattern),对方法进行增强。对此类proxy可以参考jive,其中遍布proxy
。缺点就是每一个要增加的类都要实现一个代理类,可以想象,代码量急剧膨胀。
现在让我们来看看Java Dynamic Proxy是如何达到这一点的,声明我给出的代理是示例性的,比较简陋,主要是给出一个实现方法,加以改进,即可形成一个比较通用的工具。
先来看看我们的Interface/Implemention对:
package reflection.example;
public interface Speakable {
void sayEnglish();
void sayChinese();
}
package reflection.example;
public class Translater implements Speakable {
public void sayEnglish() {
System.out.println("Hello,i am robbie");
}
public void sayChinese() {
System.out.println("你好,我是饺子");
}
}
现在我们要进行logging功能了,记住,我们只需提供一个InvocationHandler:
package reflection.example;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LoggingInvoktionHandler implements InvocationHandler {
private Object target = null;
public LoggingInvoktionHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
logging(method);
return method.invoke(target, args);
}
private void logging(Method method) {
if ("sayChinese".equals(method.getName())) {
System.out.println("翻译开始");
}
if ("sayEnglish".equals(method.getName())) {
System.out.println("Tranlation beginning");
}
}
}
这个LoggingInvocationHandler实现了InvocationHandler接中。方法的代理调用在invoke(……)中,可以如我添加logging()功能一样,加入其它的任何想要的功能,然后再进行 real target的方法调用。
如果将logging(Method method)中的逻辑抽出来,放到外部文件(XML)中进行配置读取。再对方法名采用正则表达式。是不是更为通用了呢?
下面是客户端调用:
package reflection.example;
import java.lang.reflect.Proxy;
public class Main {
/**//**
* @param args
*/
public static void main(String[] args) {
Speakable speaker = new Translater();
Speakable loggingSpeaker = (Speakable) Proxy.newProxyInstance(
speaker.getClass().getClassLoader(),
new Class[] { Speakable.class },
new LoggingInvoktionHandler(speaker));
loggingSpeaker.sayChinese();
loggingSpeaker.sayEnglish();
}
}
是不是非常简单呢?:)
如果到此为止,还是看不出Dynamic在哪儿的,想想我这儿给出的示例与spring的AOP的不同,呵呵,区别在于我这儿给出的Main亦是spring AOP的一部分,在spring里, 对某个Interface的target的代理,采用了Reflection,以达到普遍适用的,不用象我这样直接显式地给出类名而已。若我把Main做成一个Helper类,同时,把配置接口及他的实现类都放到外部XML中配置,是不是就有点象一个超级简化的spring AOP了?
当然spring不是完全用Java Dynamic Proxy的,因为Java Dynamic Proxy的前提——针对接口编程!,针对普通的类的AOP,Spring采用了字节码增强来实现。