posts - 60,comments - 71,trackbacks - 0
今天读了读夏昕写的《Spring开发指南》,在“AOP in Spring”一节,作者讲述了Spring中如何通过动态AOP来提供内置的AOP支持。“从技术角度来讲,所谓动态AOP,即通过动态Proxy模式,在目标对象的方法调用前后插入相应的处理代码”

作者举了一些例子来解释何为静态Proxy,何为动态Proxy。奈何我根基尚浅,最作者举的动态Proxy的例子懵懂不解,于是自己尝试着摸索一番,终于对动态Proxy的用法有了一个初步的认识。

首先还是来看静态Proxy,就我目前的认识看来,Proxy的主要用途是对类中方法的执行进行一下包装,在方法执行之前,或之后,或both,加入一些“额外”的操作。典型的应用就是加入事务处理。

静态Proxy相对来讲比较简单,它是对上面所述思想的一个最直接的实现。

假设我们现在有如下一个接口:

package aop;

public interface Foo {
    
public String getMessage(String arg);
}

以及该接口的一个实现类:

package aop;

public class FooImpl implements Foo {

    
public String getMessage(String arg) {
        
return "hi " + arg;
    }


}


那么很容易,我们可以通过如下方式进行调用:

package aop;

public class Test {

    
/**
     * 
@param args
     
*/

    
public static void main(String[] args) {
        Foo foo 
= new FooImpl();
        String message 
= foo.getMessage("Wing");
        System.out.println(message);
    }


}


输出结果:
hi Wing


现在问题来了:如果现在我们想在getMessage()之外添加一些额外的操作,比如在这之前输出“Begin”,在之后输出“done”,但又不能改变原有接口,怎么办?也很简单,用静态Proxy就可以很方便的实现:

package aop;

public class StaticProxy implements Foo {
    
private Foo foo;
    
    
public StaticProxy(Foo foo) {
        
this.foo = foo;
    }

    
    
public void getMessage(String arg) {
        System.out.println(
"Begin!");
        
        String message 
= foo.getMessage(arg);
        System.out.println(message);
        
        System.out.println(
"Done!");
    }

}


调用:

package aop;

public class TestStaticProxy {

    
/**
     * 
@param args
     
*/

    
public static void main(String[] args) {
        Foo foo 
= new StaticProxy(new FooImpl());
        foo.getMessage(
"Wing");
    }


}


结果:

Begin!
hi Wing
Done
!

你可以看到,StaticProxy只是在Foo的某实现类基础上包了一层,当然我们这里加的两个输出语句无足轻重,但如果我们把这两个输出语句替换成事务处理,意义就大不一样了。

可见,通过静态Proxy可以实现我们的需求,但如果我们有十几、二十个这样的接口实现类,需要类似的处理,我们怎么办?难道每个实现类都对应一个Proxy吗?

这时,我们就需要动态Proxy:

package aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class AOPHandler implements InvocationHandler {
    
    
private Object orginal;
    
    
public Object bind(Object obj) {
        orginal 
= obj;
        
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
    }


    
public Object invoke(Object proxy, Method method, Object[] args)
            
throws Throwable {
        System.out.println(
"Begin!");
        
        Object result 
= method.invoke(orginal, args);
        System.out.println(result);
        
        System.out.println(
"Done!");
        
        
return result;
    }


}



第一次看到上面的代码肯定不知所以,稍后解释,我们先看如何调用:

package aop;

public class TestAOP {

    
/**
     * 
@param args
     
*/

    
public static void main(String[] args) {
        AOPHandler handler 
= new AOPHandler();
        Object proxy 
= handler.bind(new FooImpl());
        
        String message 
= ((Foo) proxy).getMessage("Wing");
        
        System.out.println(message);
    }


}



结果:

Begin!
hi Wing
Done
!
hi Wing

下面来解释一下AOPHandler这个类:

首先,bind()方法是用来创建动态Proxy实例。动态Proxy也是Proxy,和静态Proxy一样,功能上都是为了在调用某接口实现类的方法之余,添加一些额外的操作(比如事务处理),他们(动态和静态Proxy)都是一种特殊的接口实现类。因此,我们在调用的时候可以用
String message = ((Foo) proxy).getMessage("Wing");
把proxy实例直接cast为Foo类型,并调用foo接口上的方法。

不同的是,静态Proxy,正如前面看到的,是直接实现了接口,并在接口的方法中直接调用该接口某实现类对应的方法。而动态Proxy,是利用Java.lang.reflect.Proxy类提供的这样一种机制:当构建Proxy时,我们需要提供接口,以及一个关联的调用处理程序对象(通常实现了InvocationHandler接口),当我们在Proxy上调用接口上的方法(任意方法)时,将触发调用InvocationHandler接口定义的invoke()方法,由invoke方法完成相应的操作。

上面的AOPHandler类,是将动态Proxy的构建与InvocationHandler接口的实现结合在了一起。

通过AOPHandler,我们可以在运行时期动态的决定用哪个接口实现类来创建Proxy,而不需事先为每个实现类定义对应的Proxy,灵活性和复用性大大增强。

进一步的,我们可以利用java反射机制,通过类名来得到接口实现类的实例,进而得到该实例的动态Proxy。这样我们就可以在配置文件里动态指定需要用到的接口实现类,就像Spring中所做的一样。
posted on 2008-07-10 16:07 henry1451 阅读(303) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航: