JDK 动态代理

869 阅读3分钟

参考

1、对 JDK 动态代理 使用、源码级解释
https://www.cnblogs.com/CodeBear/p/10245442.html

2、对生成JDK 动态代理代理的类的核心类 ProxyGenerator 进行了介绍
https://www.cnblogs.com/liuyun1995/p/8144706.html

概述

  • JDK 动态代理只能代理实现了接口的类,如果一个类没有继承任何的接口,那么就不能代理该类。原因是我们动态生成的所有代理类都必须继承Proxy这个类,Java的单继承特性导致。
  • 重写 java.lang.reflect.InvocationHandler#invoke方法的时候, 使用被代理类执行目标方法, 不要使用代理类, 否则会出现循环调用
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       System.out.println(">>>>>>>>>>>>>>>>>>>>>>");
       Object result = method.invoke(target, args);
       return result;
   }

1、使用方式

public class JDKDynamicProxy implements InvocationHandler {

    private Object target;

    public JDKDynamicProxy(Object target) {
        this.target = target;
    }

    /**
     * 获取被代理接口实例对象
     *
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>");
        Object result = method.invoke(target, args);
        return result;
    }

    public static void main(String[] args) {

        // 保存生成的代理类的字节码文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        OrderServiceImpl orderService = new OrderServiceImpl();
        OrderService proxy = new JDKDynamicProxy(orderService).getProxy();

        proxy.test1("xiaoming");
        proxy.test2();
    }


2、原理介绍

2.1、JDK 动态代理生成的代理类对象获取方式

  1. 调用类 ProxyGenerator的 generateProxyClass() 方法可以生成 JDK动态代理类的代理类字节码 sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[], int)

  2. 执行JDK动态代理程序时, 设置变量保存生成的代理类的字节码 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

public final class $Proxy0 extends Proxy implements OrderService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    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 void test1(String 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 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 void test2() throws  {
        try {
            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.wl.springbootdemo.demo.dynamic.jdk.OrderService").getMethod("test1", Class.forName("java.lang.String"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.wl.springbootdemo.demo.dynamic.jdk.OrderService").getMethod("test2");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

2.2、动态生成类的方式

正常方式生成一个类的方式:

  1. 拼接代理类的代码
  2. 输出.java文件
  3. 编译.java文件成.class文件
  4. 装载.class文件
  5. 创建并返回代理类对象

JDK 动态代理生成代理类的方式: JDK动态代理就是通过按照Class文件语法,程序来动态编写Class文件

  1. 拼接代理类的.class文件
  2. 装载.class文件
  3. 创建并返回代理类对象

2.3、源码分析生成代理的过程

JDK 动态代理获取代理类过程 java.lang.reflect.Proxy#newProxyInstance java.lang.reflect.Proxy#getProxyClass0 java.lang.reflect.WeakCache#get java.lang.reflect.WeakCache.Factory#get java.util.function.BiFunction#apply #java.lang.reflect.Proxy.ProxyClassFactory#apply sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[], int)

2.4、ProxyGenerator 的使用方式

JDK 动态代理代理生成代理类的核心类 ProxyGenerator, 方法 sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[]) 通过程序来动态生成Class文件的 通过反编译 ProxyGenerator 类生成的代理类的字节码,可以看到代理类原文件,了解代理的具体执行原理

public class ProxyUtils {
    /*
     * 将根据类信息 动态生成的二进制字节码保存到硬盘中,
     * 默认的是clazz目录下
     * params :clazz 需要生成动态代理类的类
     * proxyName : 为动态生成的代理类的名称
     */
    public static void generateClassFile(Class clazz, String proxyName) {
 
        //根据类信息和提供的代理类名称,生成字节码 
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
//        String paths = clazz.getResource(".").getPath();
//        System.out.println(paths);
        FileOutputStream out = null;
 
        try {
            //保留到硬盘中
            out = new FileOutputStream("/Users/chenwanli/work/keep/spring-boot-demo/spring-boot-test/src/main/java/com/wl/springbootdemo/demo/dynamic/" + proxyName + ".class");
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ProxyUtils.generateClassFile(User.class, "UserProxy");
    }
}