JDK动态代理底层源码分析

1 概述

之前分享过一篇博客是代理模式的 =》 一篇文章带你搞懂代理模式,一直没有更新JDK动态代理的底层实现,现补上…

JDK动态代理(JDK Dynamic Proxy)是Java语言提供的一种代理模式实现。它允许在运行时动态地创建代理类和代理对象,来代替原始对象进行方法的调用。在使用JDK动态代理时,主要依靠Java提供的两个关键类

  • java.lang.reflect.Proxy:JDK提供的代理对象生成工具类
  • java.lang.reflect.InvocationHandler:代理对象的回调处理器,是一个函数式接口:接收三个参数,返回一个结果

2 调试准备

  • 自定义代理对象生成器:用来记录方法执行耗时
package com.lin.tesia.proxy.jdk;

import java.lang.reflect.Proxy;

/**
 * 适用于实现了单个接口的实现类生成代理类,多个接口的话返回指定接口类型S,忽略其他接口
 *
 * @param <S> 接口
 * @param <T> 实现类
 * @author linxh
 * @date 2023/5/15 22:46
 */
public class ProxyGenerator<S, T extends S> {

   private final T target;

   public ProxyGenerator(T target) {
      this.target = target;
   }

   @SuppressWarnings("unchecked")
   public S getProxy() {
      Object proxyInstance = Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            (proxy, method, args) -> {
               long start = System.currentTimeMillis();
               Object result = method.invoke(target, args);
               long end = System.currentTimeMillis();
               System.out.println(method.getName() + "执行耗时:\t" + (end - start));
               return result;
            });
      return (S) proxyInstance;
   }
}
  • 接口和实现类:非public接口、有默认实现方法
package com.lin.tesia.proxy.jdk;

interface CalculatorService {

   int add(int x, int y);

   int sub(int x, int y);

   int mul(int x, int y);

   int div(int x, int y);

   default int mole(int x, int y) {
      return x % y;
   }
}
package com.lin.tesia.proxy.jdk;

import java.util.concurrent.TimeUnit;

/**
 * @author linxh
 * @date 2023/5/15 22:43
 */
public class CalculatorServiceImpl implements CalculatorService {

   @Override
   public int add(int x, int y) {
      try {
         TimeUnit.MILLISECONDS.sleep(1234);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      return x + y;
   }

   @Override
   public int sub(int x, int y) {
      return x - y;
   }

   @Override
   public int mul(int x, int y) {
      return x * y;
   }

   @Override
   public int div(int x, int y) {
      return x / y;
   }
}
  • main
public static void main(String[] args) {
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    ProxyGenerator<CalculatorService, CalculatorServiceImpl> proxyGenerator = new ProxyGenerator<>(new CalculatorServiceImpl());
    CalculatorService proxy = proxyGenerator.getProxy();
    System.out.println();
    System.out.println(proxy.add(3, 4));
    System.out.println();
    System.out.println(proxy.sub(6, 4));
    System.out.println();
    System.out.println(proxy.mole(9, 5));
}
  • 结果
add执行耗时:	1235
7

sub执行耗时:	0
2

mole执行耗时:	0
4

3 动态代理生成的类结构

上面测试类中通过sun.misc.ProxyGenerator.saveGeneratedFiles参数开启了JDK生成的代理对象保存在本地磁盘中,生成的类文件结构如下图

  • 包名和被代理对象的父接口包名一致(因为该接口是非public的)
  • 类名为$Proxy0,并继承自Proxy和实现了需要被代理的接口
  • 继承自Proxy,所以和Proxy类一样具备了一个有参构造函数,该参数类型为InvocationHandler,则这个参数是上面我们通过lambda表达式定义的方法耗时记录回调处理器对象
  • 具备了很多个Method对象的成员变量,具体的方法签名是在static静态代码块中在类加载的初始化阶段指定

动态代理生成及回调流程如下:

  • 当调用getProxy方法获取到的proxy虽然也是CalculatorService类型,但其实这是一个代理对象
  • 当调用proxy的方法如mole方法的时候,会把当前代理对象、原始对象的mole方法对象、参数列表回到到自定义实现的InvocationHandler中,则上面自定义的方法耗时记录回调器
  • 当回调到自定义的处理器方法中,会执行method.invoke方法(反射的方法应该都很熟了),这时运行时的具体对象就是指定的CalculatorServiceImpl对象,所以CalculatorServiceImpl的modle就是这样被拦截到的,整体流程大致是这样

在这里插入图片描述

在这里插入图片描述

4 Proxy源码分析

Proxy的源码关键在于newProxyInstance方法,该方法是创建代理对象的静态方法,涉及到代理类字节码文件的生成(这里不做说明),只对关键点说明加以解析说明

  • newProxyInstance
/**
* ClassLoader loader:类加载器
* Class<?>[] interfaces:所有的父接口
* InvocationHandler h:回调处理器
*/
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
   	// 核心方法
    Class<?> cl = getProxyClass0(loader, intfs);
	
    // 拿到了Class对象后,后面的操作就是通过反射创建cl对应的运行时的代理对象而已,不做说明
}
  • getProxyClass0:该方法是直接在缓存中通过类加载和接口列表获取代理对象,那这个proxyClassCache是什么?查看到proxyClassCache是Proxy类的一个类变量,在初始化这个对象的时候指定了两个参数:KeyFactory,和ProxyClassFactory
private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    return proxyClassCache.get(loader, interfaces);
}

private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
  • KeyFactory:KeyFactory主要用来说明当前代理对象的父类接口是有几个,一般来说只有一个顶级接口的情况是最多的
private static final class KeyFactory
    implements BiFunction<ClassLoader, Class<?>[], Object>
{
    @Override
    public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
        switch (interfaces.length) {
            case 1: return new Key1(interfaces[0]); // the most frequent
            case 2: return new Key2(interfaces[0], interfaces[1]);
            case 0: return key0;
            default: return new KeyX(interfaces);
        }
    }
}
  • ProxyClassFactory:这个对象是生成代理对象的核心,该对象和KeyFactory一样是一个BigFunction,接收两个入参(类加载器和接口列表)返回一个结果(代理对象对应的class对象),在proxyClassCache缓存中获取不到结果的时候就是回调这个方法创建,具体回调这个方法在WeakCache的内部类Factory的get方法中(这里不做说明,只需要知道这里的apply方法做了什么即可,不用管缓存的处理)
private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
    // 生成的代理对象的前缀,和下面的原子类型组合在一起,所以看到上面生成的类名是$Proxy0、$Proxy1...
    private static final String proxyClassNamePrefix = "$Proxy";
    private static final AtomicLong nextUniqueNumber = new AtomicLong();

    @Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
        // 通过IdentityHashMap来处理重复的接口,因为这里的的接口少且重复率基本没有所有使用IdentityHashMap而不是HashMap
        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        // 遍历所有的接口,其实上面也说了大多数只有一个接口哦
        for (Class<?> intf : interfaces) {
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
			// 校验是否是接口类型
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
			// 校验接口是否重复
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }

        String proxyPkg = null;     // package to define proxy class in
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

        // 再次遍历所有接口,确定要生成代理对象的包名,如果接口是public的话则使用默认包名:ReflectUtil.PROXY_PACKAGE(com.sun.proxy),如果非public如上面的接口,则使用同路径包名
        for (Class<?> intf : interfaces) {
            int flags = intf.getModifiers();
            if (!Modifier.isPublic(flags)) {
                accessFlags = Modifier.FINAL;
                String name = intf.getName();
                int n = name.lastIndexOf('.');
                String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                if (proxyPkg == null) {
                    proxyPkg = pkg;
                } else if (!pkg.equals(proxyPkg)) {
                    throw new IllegalArgumentException(
                        "non-public interfaces from different packages");
                }
            }
        }

        if (proxyPkg == null) {
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

		// 把要生成的代理对象校验都通过,并确认了包名、类名,则可以生成代理对象了,返回字节码文件回去(具体的字节码生成不作说明,我也没看,没有意义),但是在generateProxyClass方法中有一个参数是saveGeneratedFiles,这个就是我们在上面指定开启的sun.misc.ProxyGenerator.saveGeneratedFiles参数,如果开启了就会对生成的字节码文件保存一份到本地磁盘中
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            throw new IllegalArgumentException(e.toString());
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值