打赏
Fork me on GitHub

代理模式-3(手写实现JDK动态代理)

  不仅知其然,还得知其所以然。既然JDK 动态代理功能如此强大,那么他是如何实现的呢?

  我么都知道 JDK 动态代理采用字节重组,重组生成对象来替代原始对象,以达到动态代理的目的。JDK 动态代理生成对象的步骤如下:

  (1)获取被代理对象的引用,并且获取他的所有接口,反射获取。

  (2)JDK 动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口。

  (3)动态生成的 Jave 代码,新加的业务逻辑方法由一定的逻辑代码调用。

  (4)编译新生成的 Java 代码 .class 文件。

  (5)重新加载到JVM中运行。

  以上过程就叫做字节码重组。JDK 中有一个规范,在ClassPath 下只要是 $ 开头的 .class 文件,一般都是自动生成的。那么我们可以将内存中的对象字节码通过文件流输出到一个新的 .calss 文件,然后利用反编译工具查看其源代码。

  

package com.xq.design.proxy;

import com.xq.design.proxy.dynamicproxy.jdkproxy.Customer;
import com.xq.design.proxy.dynamicproxy.jdkproxy.JDKMeipo;
import com.xq.design.proxy.dynamicproxy.jdkproxy.Person;
import org.junit.jupiter.api.Test;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

public class JDKProxyTest {

    @Test
    void JDKProxyTest(){
        try{
            Person obj =(Person) new JDKMeipo().getInstance(new Customer());
            obj.findLove();
            //通过反编译工具可以查看源代码
            byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});
            FileOutputStream os = new FileOutputStream("F://$Proxy0.class");
            os.write(bytes);
            os.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

  运行以上代码,在F盘下找到 $Proxy0.class 文件。使用反编译工具反编译后得到 $Oroxy.java 文件,打开看到如下内容

  

// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space 

import com.xq.design.proxy.dynamicproxy.jdkproxy.Person;
import java.lang.reflect.*;

public final class $Proxy0 extends Proxy
    implements Person
{

    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    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 ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

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

    public final String toString()
    {
        try
        {
            return (String)super.h.invoke(this, m2, null);
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode()
    {
        try
        {
            return ((Integer)super.h.invoke(this, m0, null)).intValue();
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    static 
    {
        try
        {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                Class.forName("java.lang.Object")
            });
            m3 = Class.forName("com.xq.design.proxy.dynamicproxy.jdkproxy.Person").getMethod("findLove", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        }
        catch (NoSuchMethodException nosuchmethodexception)
        {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch (ClassNotFoundException classnotfoundexception)
        {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}
$Proxy0

  发现 $Proxy0 继承了Proxy 类,同时还实现了 Person 接口,而且重写了 findLove 方法。在静态块中用反射找到了目标对象的所有方法,而且保存了所有方法的引用,重写的方法用反射调用目标对象的方法。这些代码是JDK帮我们是生成的。

  我们也可以不依赖 JDK ,自己来动态生成源代码、动态完成编译,然后替代目标对象并执行。

  

posted @ 2020-05-10 23:51  l-coil  阅读(312)  评论(0编辑  收藏  举报