如何输出cglib以及jdk动态代理产生的class文件

背景

最近像研究下spring 原理,看到代理这块,想去看看JDK动态代理产生的过程,这里想要知道最终生成代理类的结构,于是需要知道class 文件的结构。

解决办法:

产生jdk 代理文件的方法

  • 方法一:在调用测试代理的方法前加(此方法通知虚拟保存产生的代理文件):
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 

运行后会在项目的更目录下生成.class 文件,见:
在这里插入图片描述

  • 方法二:将.class 二进制数据写到本地文件中,然后用反编译器(jd)打开就看到了。
    调式Proxy 类是发现它调用
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
              proxyName, interfaces, accessFlags);

这个类的方法产生二进制数据,并且它还有一个 public static byte[] generateProxyClass(String var0, Class<?>[] var1) 静态方法根据名称和接口对应的Class 产生二进制数组,这个就是代理类对应的.class的二进制数据,如果将这些数据写成二进制保存就好了,代码如下:

public static void testProxyGenetate() {
        byte[] newProxyClass = ProxyGenerator.generateProxyClass("$Proxy0", PersonServiceImpl.class.getInterfaces());
        System.out.println(newProxyClass);
        FileOutputStream fileOutputStream = null;
        try {
             fileOutputStream = new FileOutputStream(new File("/Users/shenjin/Desktop/$Proxy0.class"));
            try {
                fileOutputStream.write(newProxyClass);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fileOutputStream != null) {
                    try {
                        fileOutputStream.flush();
                        fileOutputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

这里注意文件名$Proxy0.class 和 generateProxyClass 传入的名称参数要相同(我一开始传的不同导致产生的文件无法打开),因为java要求类名和类的文件名相同。

输出cglib动态代理产生的类

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\class");  

产生的代理类结果

package com.leran;

import com.learn.proxy.PersonService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class proxy01 extends Proxy implements PersonService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    private static Method m0;

    public proxy01(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 boolean saveUser(String var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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 String getUserName() throws  {
        try {
            return (String)super.h.invoke(this, m4, (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.learn.proxy.PersonService").getMethod("saveUser", Class.forName("java.lang.String"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.learn.proxy.PersonService").getMethod("getUserName");
            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 代理并不神秘,基本原理就是:

  • 获取需要代理的接口的Class 对象信息
  • 生成一个新的.class文件,这个文件就是根据.class文件的生成规则动态的产生一个基于二进制格式的数组,数组本质就是.class 的二进制数组,它继承自Proxy类实现了代理接口,并且加入了通用的HashCode方法,toString,equals三个方法。
  • Proxy 调用defineClass0 这个native方法基于二进制数组产生一个Class 对象的实例,即代理对象的Class.
  • 调用cl.getConstructor(constructorParams).newInstance(new Object[]{h});返回代理类。
  • 对代理类方法的调用都会调用super.h.invoke(this, m4, (Object[])null); 因此可以在Invoke方法中进行各种拦截操作。
  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值