如何获取JDK Proxy动态代理生成的代理类源代码

如何获取JDK Proxy动态代理生成的代理类源代码

在Java中,可以使用Proxy.newProxyInstance方法来获取动态代理类,同时分析源码可知,代理类字节码文件的生成是程序运行过程中动态生成的。那么,假如我们想实际的看一下JDK动态生成的代理类里面的内容要怎么做呢?

实际上,既然已经有了代理类的字节码文件,我们只需要使用反编译工具将其转换回可读的Java源代码形式即可。而idea作为一款功能强大的集成开发环境,它提供了内置的反编译功能,可以方便地将字节码文件转换成可读的Java源代码。因此,这次我们就借助于Idea来实际地看一下JDK Proxy类生成的动态代理类的实际内容。

假设我们有一个名为UserMapper的接口类,里面包含一些自定义的查询方法。我们知道,在Mybatis中调用SqlSession的getMapper方法实际上底层就是利用Proxy.newProxyInstance方法生成了相应Mapper接口的代理类,那么本次我们就来看一下生成的代理类的内容究竟是什么样的,代码如下所示:

UserMapper映射接口类包含以下一些方法:

java 复制代码
public interface UserMapper {
    User selectUserById(@Param("id") int id);

    User selectUserByIdAndName(@Param("id") int id, @Param("username") String username);

    void update(User user);
}

Main方法代码如下所示:

java 复制代码
public class ProxyDemo {

    public static void main(String[] args) throws IOException {

      // 利用ProxyGenerator.generateProxyClass 生成相应指定类的代理类,这里指定类为UserMapper.class
      // 实际上Proxy.newProxyInstance底层也是调用该方法来生成的代理类字节码文件
        byte[] userMapper$proxies = ProxyGenerator.generateProxyClass("UserMapper$proxy", new Class[]{ UserMapper.class });

      // 写入本地文件中
        File file = new File("~/code/demo/src/main/java/com/example/demo/data/test/UserMapper$proxy.class");

        FileOutputStream fos = new FileOutputStream(file);
        fos.write(userMapper$proxies);
        fos.flush();
        fos.close();
    }

}

写入到本地文件后,我们将该文件拖入idea中,或直接用idea打开,即可看到该代理类字节码文件的反编译后的Java源代码结果,如下所示:

java 复制代码
public final class UserMapper$proxy extends Proxy implements UserMapper {
    private static Method m1;
    private static Method m2;
    private static Method m5;
    private static Method m4;
    private static Method m3;
    private static Method m0;

    public UserMapper$proxy(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 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 User selectUserByIdAndName(int var1, String var2) throws  {
        try {
            return (User)super.h.invoke(this, m5, new Object[]{var1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    public final User selectUserById(int var1) throws  {
        try {
            return (User)super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

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

    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"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m5 = Class.forName("com.netease.mail.data.dao.UserMapper").getMethod("selectUserByIdAndName", Integer.TYPE, Class.forName("java.lang.String"));
            m4 = Class.forName("com.netease.mail.data.dao.UserMapper").getMethod("selectUserById", Integer.TYPE);
            m3 = Class.forName("com.netease.mail.data.dao.UserMapper").getMethod("update", Class.forName("com.netease.mail.data.pojo.User"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到,代理类继承了Proxy父类,实现了传入的接口类,并实现了最基本的equlas、toString、hashCode方法。在这些方法中,可以看到都是调用了super.h.invoke方法,这也就是我们常说的,为什么代理类调用方法时,实际上都会去执行绑定的InvocationHandler的invoke方法。

同时,为了在调用super.h.invoke方法时传入实际被调用的方法,代理类中定义了若干Method类型的属性,并在static静态代码块中利用反射进行初始化。这些Method类型的变量实际上也就是对应了代理类中需要实现的代理接口方法,以及Object类基础的equlas、toString、hashCode方法。

这样,在调用代理类的方法时,实际上就会执行super.h.invoke方法,在invoke方法中利用反射来执行相应的方法,并实现在不改变原方法的前提下,新增额外的处理逻辑

至此,我们就实现了针对代理类字节码文件,利用反编译获取其Java源代码格式,并分析了代理类的具体内容,加深了对代理类的实现原理和执行逻辑的理解。

相关推荐
惜鸟9 分钟前
Spring Boot项目自己封装一个分页查询工具
spring boot·后端
Dithyrambus9 分钟前
ObjectScript 中文入门教程
后端
北执南念11 分钟前
JDK 动态代理和 Cglib 代理的区别?
java·开发语言
盛夏绽放24 分钟前
Python 目录操作详解
java·服务器·python
贰拾wan24 分钟前
ArrayList源码分析
java·数据结构
Code季风26 分钟前
跨语言RPC:使用Java客户端调用Go服务端的JSON-RPC服务
java·网络协议·rpc·golang·json
林太白34 分钟前
也许看了Electron你会理解Tauri,扩宽你的技术栈
前端·后端·electron
松果集40 分钟前
【Python3】练习一
后端
anganing40 分钟前
Web 浏览器预览 Excel 及打印
前端·后端
肯定慧44 分钟前
B1-基于大模型的智能办公应用软件
后端