Kelvin的胡言乱语

==============> 重剑无锋,大巧不工。

动态代理与拦截器的实现原理剖析

这是我第二个博客其中的一篇文章。

下面是原文(未大改,稍作了一些格式上的调整):


对动态代理的学习是从Struts 2的拦截器的学习引出来的,Struts 2的拦截器的实现即借用了动态代理,于是我又Google了一下动态代理的实现原理,看了几段代码,但终究对程序的运行原理似懂非懂,为彻底弄清动态代理的原理,我阅读了 java.lang.reflect.Proxy 类的源代码。

Proxy类中有两个关键方法,一个是:

public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)

另一个是:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

newProxyInstance() 中调用了 getProxyClass() ,但在 getProxyClass() 中由于native方法 defineClass0() 的存在,对这段代码的研究也未能彻底弄懂,不过还是大致弄懂了它的作用:程序传给它一个类加载器,若干个接口,它的作用就是利用这个类加载器,动态生成一个实现这若干个接口的类,并返回。

下面再看 newProxyInstance() 方法,它有如下关键代码(其中省略了必要的try-catch语句块):

Class cl = getProxyClass(loader, interfaces);
Constructor cons = cl.getConstructor(constructorParams);
// constructorParams的定义为:private final static Class[] constructorParams = {InvocationHandler.class};
return (Object)cons.newInstance(new Object[]{h});

可看出它首先是得到一个实现了参数中所列接口的类,通过第二句代码,可以猜出该类必定有一个构造方法为: public Proxy(Class[]{InvocationHandler h}) ,通过第三句代码可以猜出,该类中定义了一个字段为: private InvocationHandler h; ,并且将参数中的InvocationHandler的一个引用传给该字段以创建一个新实例,然后返回该实例。

从动态代理的作用可以猜出,这个返回类实现的所有方法,必定都只有一句代码: return h.invoke(this, ..., ...); 即所有方法都只调用了InvacationHandler实例的invoke方法,当然这个类中还要处理如何获得相应方法的封装类Method以及其形参args(Object[]类型),并且作为参数传递给invoke()。

好了,既然原理搞清楚了,下面就来做一个动态代理的实现示例:

package ini.always.DynamicProxy;

import java.lang.reflect.*;

interface Logic
{
    public void doSomeBusinessLogic();
}

class BusinessLogic implements Logic
{
    public void doSomeBusinessLogic()
    {
        System.out.println("in method doSomeBusinessLogic()");
    }
}

class ProxyHandler implements InvocationHandler
{
    private Object target;

    public ProxyHandler(Object target)
    {
        this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Exception
    {
        Object result;
        before();
        result = method.invoke(target, args);
        after();
        return result;
    }
    private void before()
    {
        System.out.println("before method doSomeBusinessLogic()");
    }
    private void after()
    {
        System.out.println("after method doSomeBusinessLogic()");
    }
}

public class TestProxy
{
    public static void main(String[] args)
    {
        Logic logic = new BusinessLogic();
        InvocationHandler h = new ProxyHandler(logic);
        Logic proxy = (Logic) Proxy.newProxyInstance(Logic.class.getClassLoader(),logic.getClass().getInterfaces(), h);
        proxy.doSomeBusinessLogic();
    }
}

编译后运行,程序将输出:

before method doSomeBusinessLogic()
in method doSomeBusinessLogic()
after method doSomeBusinessLogic()

这就是动态代理和拦截器的实现原理。

Comments

comments powered by Disqus