JDK动态代理(3)

参考博客:

https://www.cnblogs.com/liuyun1995/p/8144628.html

https://www.cnblogs.com/liuyun1995/p/8157098.html

https://www.cnblogs.com/liuyun1995/p/8144676.html

https://www.cnblogs.com/liuyun1995/p/8144706.html

https://blog.csdn.net/wangqyoho/article/details/77584832

getProxyClass0()方法

在通过newProxyInstance()方法获取代理对象的过程中 , 有一个比较关键的步骤 , 就是通过getProxyClass0()获取代理类

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        ...
        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);
        /*
         * Invoke its constructor with the designated invocation handler.
         */         
    }

跟进这个方法可以看到首先是一个接口数量的检查,然后是从一个二级缓存中获取代理类,如果缓存中不存在该代理类,则会调用ProxyClassFactory这个工厂去生成一个代理类。

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

ProxyClassFactory工厂类 :

ProxyGenerator.generateProxyClass(proxyName,
                                  interfaces, accessFlags)//代理类生成工厂
private static final class ProxyClassFactory 
                implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
    //代理类名称前缀
    private static final String proxyClassNamePrefix = "$Proxy";
    //用原子类来生成代理类的序号, 以此来确定唯一的代理类
    private static final AtomicLong nextUniqueNumber = new AtomicLong();
    @Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        for (Class<?> intf : interfaces) {
          //这里遍历interfaces数组进行验证, 主要做三件事情
          Class<?> interfaceClass = null;
          try {
            interfaceClass = Class.forName(intf.getName(), false, loader);
          } catch (ClassNotFoundException e) {
          }
           //1.intf是否可以由指定的类加载进行加载
          if (interfaceClass != intf) {
            throw new IllegalArgumentException(
              intf + " is not visible from class loader");
          }
          //2.intf是否是一个接口
          if (!interfaceClass.isInterface()) {
            throw new IllegalArgumentException(
              interfaceClass.getName() + " is not an interface");
          }
          //3.intf在数组中是否有重复
          if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
            throw new IllegalArgumentException(
              "repeated interface: " + interfaceClass.getName());
          }
        }
        //生成代理类的包名
        String proxyPkg = null;
        //生成代理类的访问标志, 默认是public final的
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
        for (Class<?> intf : interfaces) {
            //获取接口的访问标志
            int flags = intf.getModifiers();
            //如果接口的访问标志不是public, 那么生成代理类的包名和接口包名相同
            if (!Modifier.isPublic(flags)) {
                //生成的代理类的访问标志设置为final
                accessFlags = Modifier.FINAL;
                //获取接口全限定名, 例如:java.util.Collection
                String name = intf.getName();
                int n = name.lastIndexOf('.');
                //剪裁后得到包名:java.util
                String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                //生成的代理类的包名和接口包名是一样的
                if (proxyPkg == null) {
                    proxyPkg = pkg;
                } else if (!pkg.equals(proxyPkg)) {
                    //代理类如果实现不同包的接口, 并且接口都不是public的, 那么就会在这里报错
                    throw new IllegalArgumentException(
                        "non-public interfaces from different packages");
                }
            }
        }
        //如果接口访问标志都是public的话, 那生成的代理类都放到默认的包下:com.sun.proxy
        if (proxyPkg == null) {
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }
        //生成代理类的序号
        long num = nextUniqueNumber.getAndIncrement();
        //生成代理类的全限定名, 包名+前缀+序号, 例如:com.sun.proxy.$Proxy0
        String proxyName = proxyPkg + proxyClassNamePrefix + num;
        //这里是核心, 用ProxyGenerator来生成字节码, 该类放在sun.misc包下
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,
                                  interfaces, accessFlags);
        try {
            //根据二进制文件生成相应的Class实例
            return defineClass0(loader, proxyName, proxyClassFile, 
                              0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            throw new IllegalArgumentException(e.toString());
        }
    }
}
  • 代理类工厂生成代理的的过程主要可以分为三步步 :

    1. 对要代理的接口进行验证(类加载器是否可以加载数组中的接口 , 数组中的对象是否都是接口 , 接口数组是否存在重复接口)

    2. 设置代理类的包名 ,访问标志 , 类名称 ,并通过ProxyGenerator.generateProxyClass()方法组装生成字节码文件

      1. 代理类的全限定名 = 报名 + $proxy + 序号

      2. 如果接口是public的,代理类默认是public final的,并且生成的代理类默认放到com.sun.proxy这个包下。

      3. 如果接口是非public的,那么代理类也是非public的,并且生成的代理类会放在对应接口所在的包下。

      4. 如果接口是非public的,并且这些接口不在同一个包下,那么就会报错。

    3. 通过defineClass0()方法根据二进制文件生成相应的Class实例

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值