2.JDK动态代理类字节码的生成与解读

263 阅读3分钟

获取动态代理类字节码

如果想要拿到动态代理类的源码,需要做以下操作:

在这一行代码前面

Proxy.newProxyInstance(loader, interfaces, h);

添加如下代码

 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

完整代码

package jdk代理;

import java.lang.reflect.Proxy;
/***
 * @author: claude
 * @date: 2023/3/1
 * @description:
 */
public class CalculatorProxyFactory{

    public static Object getProxy(CalculatorConcrete calculator) {
        ClassLoader loader = calculator.getClass().getClassLoader();
        Class<?>[] interfaces = calculator.getClass().getInterfaces();
        CalculatorInvocationHandler h = new CalculatorInvocationHandler(calculator);
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Object proxy = Proxy.newProxyInstance(loader, interfaces, h);
        return proxy;
    }

    public static void main(String[] args) {
        AddAndSubCalculator addAndSubCalculator = (AddAndSubCalculator) getProxy(new CalculatorConcrete());
        MultiplyAndDivCalculator multiplyAndDivCalculator = (MultiplyAndDivCalculator) getProxy(new CalculatorConcrete());
        addAndSubCalculator.add(1, 1);
        System.out.println("----------------------------");
        multiplyAndDivCalculator.div(4, 4);
    }
}

或者在idea的VM options中 设置如下参数:

-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

然后生成我们的代理类即可,默认生成的包的路径是:com.sun.proxy。

package jdk代理;

import java.lang.reflect.Proxy;
/***
 * @author: claude
 * @date: 2023/3/1
 * @description:
 */
public class CalculatorProxyFactory{

    public static Object getProxy(CalculatorConcrete calculator) {
        ClassLoader loader = calculator.getClass().getClassLoader();
        Class<?>[] interfaces = calculator.getClass().getInterfaces();
        CalculatorInvocationHandler h = new CalculatorInvocationHandler(calculator);
        Object proxy = Proxy.newProxyInstance(loader, interfaces, h);
        return proxy;
    }

    public static void main(String[] args) {
        AddAndSubCalculator addAndSubCalculator = (AddAndSubCalculator) getProxy(new CalculatorConcrete());
        MultiplyAndDivCalculator multiplyAndDivCalculator = (MultiplyAndDivCalculator) getProxy(new CalculatorConcrete());
        addAndSubCalculator.add(1, 1);
        System.out.println("----------------------------");
        multiplyAndDivCalculator.div(4, 4);
    }
}

执行完毕之后可以看到生成了1个$Proxy0.class。

反编译动态代理类字节码

将$Proxy0.class直接拖到idea即可反编译。

package com.sun.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import jdk代理.AddAndSubCalculator;
import jdk代理.MultiplyAndDivCalculator;

public final class $Proxy0 extends Proxy implements AddAndSubCalculator, MultiplyAndDivCalculator {
    private static Method m1;
    private static Method m5;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m6;
    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 int multiply(int var1, int var2) throws  {
        try {
            return (Integer)super.h.invoke(this, m5, new Object[]{var1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    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 int add(int var1, int var2) throws  {
        try {
            return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

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

    public final int div(int var1, int var2) throws  {
        try {
            return (Integer)super.h.invoke(this, m6, new Object[]{var1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    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"));
            m5 = Class.forName("jdk代理.MultiplyAndDivCalculator").getMethod("multiply", Integer.TYPE, Integer.TYPE);
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("jdk代理.AddAndSubCalculator").getMethod("add", Integer.TYPE, Integer.TYPE);
            m4 = Class.forName("jdk代理.AddAndSubCalculator").getMethod("sub", Integer.TYPE, Integer.TYPE);
            m6 = Class.forName("jdk代理.MultiplyAndDivCalculator").getMethod("div", Integer.TYPE, Integer.TYPE);
            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代理原理概览

1.$Proxy0继承自Proxy,在Proxy中有类型为InvocationHandler的成员变量。

protected InvocationHandler h;

Proxy中存在1个带参构造方法,参数是InvocationHandler。

Proxy(InvocationHandler h) {
    Objects.requireNonNull(h);
    this.h = h;
}

调用$Proxy0的构造方法会调用Proxy的有参构造。从而把自定义的CalculatorInvocationHandler传递给父类。

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

在自定义的CalculatorInvocationHandler中我们定义了一个成员变量calculatorConcrete,为被代理类的实例

2.调用代理类的add方法,

public static void main(String[] args) {
        AddAndSubCalculator addAndSubCalculator = (AddAndSubCalculator) getProxy(new CalculatorConcrete());
        addAndSubCalculator.add(1, 1);
    }

其实就是调用$Proxy0#add。

生成的代理类$Proxy0会继承Proxy并实现AddAndSubCalculator, MultiplyAndDivCalculator接口。

并重写AddAndSubCalculator, MultiplyAndDivCalculator接口的所有方法。

以add方法为例实现如下:

在生成的代理类Proxy0中会使用反射获取到被代理类CalculatorConcrete所有实现的接口的method对象,并作为代理类Proxy0中会使用反射获取到被代理类CalculatorConcrete所有实现的接口的method对象,并作为代理类Proxy0的成员变量。

private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m5;
    private static Method m6;
    private static Method m0;
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("jdk代理.AddAndSubCalculator").getMethod("add", Integer.TYPE, Integer.TYPE);
            m4 = Class.forName("jdk代理.AddAndSubCalculator").getMethod("sub", Integer.TYPE, Integer.TYPE);
            m5 = Class.forName("jdk代理.MultiplyAndDivCalculator").getMethod("multiply", Integer.TYPE, Integer.TYPE);
            m6 = Class.forName("jdk代理.MultiplyAndDivCalculator").getMethod("div", Integer.TYPE, Integer.TYPE);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

    public final int add(int var1, int var2) throws  {
            try {
                //super是Proxy,h是CalculatorInvocationHandler
                //this是代理类对象
                //m3是接口AddAndSubCalculator#add方法
                // new Object[]{var1, var2}是入参
                return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});
            } catch (RuntimeException | Error var4) {
                throw var4;
            } catch (Throwable var5) {
                throw new UndeclaredThrowableException(var5);
            }
        }

可以看到主要是这一句

		//super是Proxy,h是CalculatorInvocationHandler
                //this是代理类对象$Proxy0
                //m3是接口AddAndSubCalculator#add方法
                // new Object[]{var1, var2}是入参
                return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});

由于我们的代理类$Proxy0继承了Proxy,所以我们自定义的CalculatorInvocationHandler可以通过super.h访问到。

所以在调用代理类的方法时,调用的总是CalculatorInvocationHandler的invoke方法。

所以在调用代理类的方法时,调用的总是CalculatorInvocationHandler的invoke方法。

所以在调用代理类的方法时,调用的总是CalculatorInvocationHandler的invoke方法。

5.在CalculatorInvocationHandler#invoke方法中。我们可以使用被代理类的实例CalculatorConcrete通过反射调用到被代理类CalculatorConcrete的原始方法,从而达到在被代理类CalculatorConcrete的方法前后动态织入代码的目的。

public class CalculatorInvocationHandler implements InvocationHandler {
    private CalculatorConcrete calculatorConcrete;

    public CalculatorInvocationHandler(CalculatorConcrete calculatorConcrete) {
        this.calculatorConcrete = calculatorConcrete;
    }
    
    //参数1:即代理类对象
    //参数2:即m3,是接口AddAndSubCalculator#add方法
    //参数3:new Object[]{var1, var2}是入参
    //calculatorConcrete是CalculatorInvocationHandler的成员变量
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("开始计算");
        Object result = method.invoke(calculatorConcrete, args);
        System.out.println("结束计算:" + result);
        return result;
    }
}