1.JDK动态代理原理概览

77 阅读3分钟

代理要解决的问题

如何调用到被代理类的原始方法?

jdk代理代码

package jdk代理;
/***
 * @author: claude
 * @date: 2023/3/1
 * @description:
 */
public interface AddAndSubCalculator {
    /***
     * 加法
     * @param i
     * @param j
     * @return
     */
    public int add(int i, int j);

    /***
     * 减法
     * @param i
     * @param j
     * @return
     */
    public int sub(int i, int j);

}
package jdk代理;
/***
 * @author: claude
 * @date: 2023/3/1
 * @description:
 */
public interface MultiplyAndDivCalculator {
    /****
     * 乘法
     * @param i
     * @param j
     * @return
     */
    public int multiply(int i, int j);

    /****
     * 除法
     * @param i
     * @param j
     * @return
     */
    public int div(int i, int j);
}
package jdk代理;

/***
 * @author: claude
 * @date: 2023/3/1
 * @description:
 */
public class CalculatorConcrete implements AddAndSubCalculator, MultiplyAndDivCalculator {
    @Override
    public int add(int i, int j) {
        int result = i + j;
        System.out.println("计算结果:" + result);
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result = i - j;
         System.out.println("计算结果:" + result);
        return result;
    }

    @Override
    public int multiply(int i, int j) {
        int result = i * j;
         System.out.println("计算结果:" + result);
        return result;
    }

    @Override
    public int div(int i, int j) {
        int result = i / j;
         System.out.println("计算结果:" + result);
        return result;
    }
}
package jdk代理;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author: claude-彭方亮
 * @package: jdk代理.MyInvocationHandler
 * @date: 2023/3/1 7:55
 * @description:
 * @version: 1.0
 */
public class CalculatorInvocationHandler implements InvocationHandler {
    private CalculatorConcrete calculatorConcrete;

    public CalculatorInvocationHandler(CalculatorConcrete calculatorConcrete) {
        this.calculatorConcrete = calculatorConcrete;
    }

    @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;
    }
}
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);
    }
}

jdk代理原理概览

1.所有通过jdk代理生成的代理类会继承Proxy,在Proxy中有类型为InvocationHandler的成员变量。

protected InvocationHandler h;

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

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

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

3.调用Proxy.newProxyInstance(loader, interfaces, invocationHandler);

会把CalculatorInvocationHandler作为参数调用代理类的构造方法构建1个Proxy对象并返回,这就是我们的代理类对象。

同时由于我们的代理类继承了Proxy,所以CalculatorInvocationHandler可以通过super.h访问到。

4.在生成的代理类中会使用反射获取到代理类的接口的method对象,并作为代理类的成员变量。

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());
        }
    }

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

并重写AddAndSubCalculator, MultiplyAndDivCalculator接口的所有方法,以add方法为例实现如下:

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);
        }
    }

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

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

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

6.在CalculatorInvocationHandler#invoke方法中,我们可以使用被代理类的实例使用反射调用到被代理类的原始方法。

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;
    }
}

7.通过invocationHandler#invoke方法在原始类的原始方法前后动态织入代码。