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


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

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

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

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

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

    public interface UserMapper {
        User selectUserById(@Param("id") int id);
    
        User selectUserByIdAndName(@Param("id") int id, @Param("username") String username);
    
        void update(User user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Main方法代码如下所示:

    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();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    写入到本地文件后,我们将该文件拖入idea中,或直接用idea打开,即可看到该代理类字节码文件的反编译后的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());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

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

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

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

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

  • 相关阅读:
    SQL注入漏洞及五大手法
    3 基于采样的路径规划 —— RRT算法
    Android 插件化
    【机器学习300问】60、图像分类任务中,训练数据不足会带来什么问题?如何缓解图像数据不足带来的问题?
    08项目质量管理
    我的y9000p-i9-12900h购买之旅
    【云原生】· 一文了解docker中的网络
    用于YOLO格式分割的咖啡叶病害数据集。
    十大排序算法(C++)
    卷积神经网络
  • 原文地址:https://blog.csdn.net/Urbanears/article/details/134554872