动态代理原理

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

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->before
hello
CalOpInterceptor->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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值