动态代理原理

来源:互联网 发布:linux c 获取时间 格式 编辑:程序博客网 时间:2024/05/16 13:55

先看一个动态代理的小例子

1,先创建一个接口CalOp和实现了这个接口的类CalOpImpl

public interface CalOp {    void show();}
public class CalOpImpl implements CalOp {    public void show() {        System.out.println("hello");    }}

2,创建一个拦截器CalOpInterceptor

public class CalOpInterceptor {    public void before() {        System.out.println("CalOpInterceptor->before");    }    public void after() {        System.out.println("CalOpInterceptor->after");    }}

3,代理类CalOpProxy

public class CalOpProxy implements InvocationHandler {    private Object object;    private CalOpInterceptor interceptor ;    public Object bind(Object object,CalOpInterceptor interceptor) {        this.object = object;        this.interceptor = interceptor;        return Proxy.newProxyInstance(this.object.getClass().getClassLoader(),                this.object.getClass().getInterfaces(), this);    }    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        interceptor.before();        Object result = method.invoke(this.object, args);        interceptor.after();        return result;    }}

然后,我们就可以写一个main方法来测试一下

public class JRun {    public static void main(String[] args) {        CalOp calOp = new CalOpImpl();        CalOpInterceptor interceptor = new CalOpInterceptor();        CalOpProxy proxy = new CalOpProxy();//        创建一个代理对象        CalOp proxyCalOp = (CalOp) proxy.bind(calOp, interceptor);        proxyCalOp.show();    }}

运行,输出如下。跟我们想要的是一样的

CalOpInterceptor->beforehelloCalOpInterceptor->after

动态代理原理

在JRun的第7行(如下),我们生成了一个代理类对象proxyCalOp。

CalOp proxyCalOp = (CalOp) proxy.bind(calOp, interceptor);

然后通过访问proxyCalOp来实现对calOp的访问。让我们进入代码看一下这个代理类对象是怎么生成的。
bind方法

public Object bind(Object object,CalOpInterceptor interceptor) {        this.object = object;        this.interceptor = interceptor;        return Proxy.newProxyInstance(this.object.getClass().getClassLoader(),                this.object.getClass().getInterfaces(), this);    }

Proxy的newProxyInstance

 public static Object newProxyInstance(ClassLoader loader,                                          Class<?>[] interfaces,                                          InvocationHandler h)        throws IllegalArgumentException    {    /*    * loader = 加载CalOp的classloader对象.因为是生成的新的代理类要赋值给一个CalOp对象。如果不是跟CalOp使用同一个classloader,会出现类型转换异常    * interfaces = CalOp这个接口。只会实现接口中的方法。如果多个接口中的方法重名,则只代理声明靠前的接口    * h = 一个CalOpProxy类的实例,对应bind方法中的this    */    // 暂时先不管,后面会提到        if (h == null) {            throw new NullPointerException();        }        final Class<?>[] intfs = interfaces.clone();        final SecurityManager sm = System.getSecurityManager();        if (sm != null) {            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);        }        /*         * Look up or generate the designated proxy class.         * 生成一个新的类,该类实现了interface的所有接口。这里就是实现了CalOp的所有接口。具体实现下面会看到         * 并且由加载CalOp的classloader加载         *         */        Class<?> cl = getProxyClass0(loader, intfs);        /*         * Invoke its constructor with the designated invocation handler.         */         // 下面使用反射,调用构造函数生成该类的实例。这个类的实例就是代理类的实例,就是JRun中的proxyCalOp对象        try {            final Constructor<?> cons = cl.getConstructor(constructorParams);            final InvocationHandler ih = h;            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {                // create proxy instance with doPrivilege as the proxy class may                // implement non-public interfaces that requires a special permission                return AccessController.doPrivileged(new PrivilegedAction<Object>() {                    public Object run() {                        return newInstance(cons, ih);                    }                });            } else {                return newInstance(cons, ih);            }        } catch (NoSuchMethodException e) {            throw new InternalError(e.toString());        }    }

在上面,newProxyInstance函数的

Class<?> cl = getProxyClass0(loader, intfs);

中生成了一个代理类。我们可以通过一些方法拿到这个代理类的class文件,通过反编可以得到代理类的结构,如下。

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.// Jad home page: http://www.kpdus.com/jad.html// Decompiler options: packimports(3) package com.sun.proxy;import com.proxytest.CalOp;import java.lang.reflect.*;public final class $Proxy0 extends Proxy    implements CalOp{    public $Proxy0(InvocationHandler invocationhandler)    {        super(invocationhandler);    }    public final boolean equals(Object obj)    {        try        {            return ((Boolean)super.h.invoke(this, m1, new Object[] {                obj            })).booleanValue();        }        catch(Error _ex) { }        catch(Throwable throwable)        {            throw new UndeclaredThrowableException(throwable);        }    }    public final void show()    {        try        {            super.h.invoke(this, m3, null);            return;        }        catch(Error _ex) { }        catch(Throwable throwable)        {            throw new UndeclaredThrowableException(throwable);        }    }    public final int hashCode()    {        try        {            return ((Integer)super.h.invoke(this, m0, null)).intValue();        }        catch(Error _ex) { }        catch(Throwable throwable)        {            throw new UndeclaredThrowableException(throwable);        }    }    public final String toString()    {        try        {            return (String)super.h.invoke(this, m2, null);        }        catch(Error _ex) { }        catch(Throwable throwable)        {            throw new UndeclaredThrowableException(throwable);        }    }    private static Method m1;    private static Method m3;    private static Method m0;    private static Method m2;    static     {        try        {            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {                Class.forName("java.lang.Object")            });            m3 = Class.forName("com.proxytest.CalOp").getMethod("show", new Class[0]);            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);        }        catch(NoSuchMethodException nosuchmethodexception)        {            throw new NoSuchMethodError(nosuchmethodexception.getMessage());        }        catch(ClassNotFoundException classnotfoundexception)        {            throw new NoClassDefFoundError(classnotfoundexception.getMessage());        }    }}

现在我们就应该很清楚,在JRun中的代理类对象proxyCalOp就是$Proxy0类的实例

CalOp proxyCalOp = (CalOp) proxy.bind(calOp, interceptor);proxyCalOp.show();

proxyCalOp.show()的调用实际调用的是:

public final void show()    {        try        {            super.h.invoke(this, m3, null);            return;        }        catch(Error _ex) { }        catch(Throwable throwable)        {            throw new UndeclaredThrowableException(throwable);        }    }

我们通过跟踪参数传递,可以知道h就是CalOpProxy类的实例,那么super.h.invoke(this, m3, null)调用的就是CalOpProxy的方法

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        interceptor.before();        Object result = method.invoke(this.object, args);        interceptor.after();        return result;    }

到此,就真相大白了。

0 0
原创粉丝点击