web框架中用到的设计模式专题

源码阅读JDK动态代理底层实现

2019-06-20  本文已影响0人  先生zeng

上次更新了一篇设计模式之代理模式,今天仔细的了解了一下JDK实现的原理,又看了一下网上其实很多人都说自己也能实现一个代理模式,手痒就敲了一下。
想要学习完整代理模式的可以看我前几篇更新的文章:
https://www.jianshu.com/p/bee8a547faa3

动态代理主要有两种,一种是JDk提供的,代理类需要实现InvocationHandle接口,然后内部在构造方法去返回一个Proxy生成的代理类实例,然后还要重新invoke这个方法,在这个方法中实现代理类想要做的事。

在客户端获取这个代理类之后,直接调用方法就可以了。
另外一种就是通过CGLib实现的。这个我下次再来研究,这次是实现的JDK的动态代理模式。

可以先来贴一段代码(按注释编号读):

//1.这是一个被代理类
public class XieMu implements Person {

    @Override
    public void findLove(){
        System.out.println("高富帅");
        System.out.println("身高180cm");
        System.out.println("胸大,6块腹肌");
    }

    @Override
    public void zufangzi() {
        System.out.println("租房子");
    }

    @Override
    public void buy() {
        System.out.println("买东西");
    }

    @Override
    public void findJob() {
        System.out.println("月薪20K-50k");
        System.out.println("找工作");
    }
}

// 2.这是一个媒婆的代理类
public class JDKMeipo implements InvocationHandler{
    //被代理的对象,把引用给保存下来
    private Person target;

    public Object getInstance(Person target) throws Exception{
        this.target = target;

       Class<?> clazz = target.getClass();

        //下半截,深入底层来给大家讲解字节码是如何重组的
        //用来生成一个新的对象(字节码重组来实现)
        /** 
        *   JDK提供了一个Proxy.newProxyInstance(代理类的类加载器,代理类实现的接口,代理类本身)我们直接调用,他就会帮我们在运行时动态生成一个代理类。
        */
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);

//他的客户端实现如下:
//3可以看到,通过媒婆这个类返回了一个代理类,这个类跟被代理类实现了同样的接口Person,返回Person
Person obj = (Person)new JDKMeipo().getInstance(new XieMu());
            System.out.println(obj.getClass());
            obj.findJob();
    }

可以看到,客户端只需要通过这个媒婆类的getInstance,就可以返回一个我们被代理类实现的接口的代理类。然后我们深入Proxy.newProxyInstance来看一下:

@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
        //浅复制了被代理类实现的接口
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
          //检查创建Proxy类所需的权限
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 查找或生成制定的代理类
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         *使用指定的调用处理程序调用其构造函数
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
//重点来了,反射获取constructorParams,也就是InvocationHandler.class类的构造器
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;  //看了上下文,发现没意义。
            //获取此类cl,即代理类的java语言修饰符,判断如果不是public的访问域,则设置可以setAccessible(true);访问
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
//以上的设置,是为了下面代理类的构造器新生成一个代理类实例,通过反射去生成
            return cons.newInstance(new Object[]{h});

然后接这cons.newInstance(new Object[]{h});继续看:
可以通过双亲委派模式,去判断,最终是这里实现了新建一个对象。

return UnsafeFieldAccessorImpl.unsafe.allocateInstance(this.constructor.getDeclaringClass());

然后来看,那么我们实现InvocationHandle,重新invoke跟这个Proxy有什么关联呢? 再来看一下。Proxy类有一个这样的注释:

An interface method invocation on a proxy instance will be
 * encoded and dispatched to the invocation handler's {@link
 * InvocationHandler#invoke invoke} method as described in the
 * documentation for that method
代理实例上的接口方法调用将被编码并调度到调用处理程序的{@link * InvocationHandler#invoke invoke}方法,如该方法的*文档中所述

最后我们再来看一下,通过Proxy生成的代理类打印出来是什么?

class com.sun.proxy.$Proxy0  //JDK中有个规范,只要要是$开头的一般都是自动生成的,所以肯定是Proxy自动生成一个代理类后,再加载到JVM去运行的。

 //通过反编译工具可以查看源代码
            byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});
            FileOutputStream os = new FileOutputStream("E://$Proxy0.class");
            os.write(bytes);
            os.close();

查看到的代码如下:
import com.zxy.test.gupao.tom.proxy.staticed.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m6;
    private static Method m4;
    private static Method m5;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void findLove() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void zufangzi() throws  {
        try {
            super.h.invoke(this, m6, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void buy() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void findJob() throws  {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.zxy.test.gupao.tom.proxy.staticed.Person").getMethod("findLove");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m6 = Class.forName("com.zxy.test.gupao.tom.proxy.staticed.Person").getMethod("zufangzi");
            m4 = Class.forName("com.zxy.test.gupao.tom.proxy.staticed.Person").getMethod("buy");
            m5 = Class.forName("com.zxy.test.gupao.tom.proxy.staticed.Person").getMethod("findJob");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

以下是我总结的JDK底层实现JDK的原理:

//原理:
            //1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取
            //2、JDK Proxy类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口
            //3、动态生成Java代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)
            
            //4、编译新生成的Java代码.class
            //5、再重新加载到JVM中运行
            //以上这个过程就叫字节码重组

好了,以上我已经总结了JDK底层是如何实现动态代理的了,那么下面我将自己来实现JDK动态代理。
可以看我的下一篇博客哦:
地址:
https://www.jianshu.com/p/9608c628f823

感谢您阅读我的文章,如果满意可以帮我点赞,谢谢哈。
如果对文章部分还有什么见解或者疑惑,可以私信评论我,欢迎技术讨论。如果需要获取完整的文件资源,可以加我微信z985085305,获取我整理的全套笔记。
思想的碰撞最能促进技术的进步哦。

上一篇 下一篇

猜你喜欢

热点阅读