深入理解动态代理源码(3)

/*
        * Invoke its constructor with the designated invocation handler.
        */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

上面的代码由下来解释:

2.3.1:安全管理器检查与代理权限检查

2.3.2:判断接口的长度是否大于65535,大于不可往下进行。然后从WeakCache缓存中获取代理类,如果找不到则通过proxyClassFactory生成代理类

2.3.2.1:生成代理类的class过程如下:

1⃣️验证传入的interface是否可被传入的classloader装载

2⃣️验证传入的是否是一个接口,如果不是一个接口,直接抛出IllegalArgumentException异常

3⃣️判断传入的是否重复,这里是通过一个IdentityHashMap往里面put接口的class类,如果返回值不为null表示这个接口已经注册过了(如果第一次put会返回null,按照传统的做法是先get是否为null,如果不为null再put,这行代码很妙省去了这个步骤),IdentityHashMap也是map的一种,不过它与我们普通的HashMap最大的不同在于它不会通过equals方法和hashCode方法去判断key是否重复,而是通过==运算符

4⃣️拼接代理类的名字固定为:com.sun.proxy.$Proxy+原子自增序号,为了防止并发调用,在生成代理类名字的时候,采用了AtomicLong的getAndIncrement方法去原子递增代理类的序列号,这个方法是原子的,所以不会产生并发问题。这里也就是我们为什么看到最后的代理类是$Proxy0的原因(生成的代理类的序号是从0开始的)

5⃣️调用ProxyGenerator.generateProxyClass方法来生成代理的class类(过程较为复杂、通过一些jvm指令去生成字节码,包括遍历方法类型、返回值、参数类型等)

6⃣️通过defineClass将上一步产生的class字节码生成class文件,该方法也是一个native方法,需要传入类的classloader来进行装载生成的类字节码进而生成class

2.3.3: 通过反射获取构造器constractor创建一个反射实例,这个过程进行了强制构造器的private私有化反射

三:几个相关的问题

3.1:为什么动态代理需要传入classLoader?

主要原因有以下几个:

1⃣️需要校验传入的接口是否可被当前的类加载器加载,假如无法加载,证明这个接口与类加载器不是同一个,按照双亲委派模型,那么类加载层次就被破坏了

2⃣️需要类加载器去根据生成的类的字节码去通过defineClass方法生成类的class文件,也就是说没有类加载的话是无法生成代理类的

3.2:为什么动态代理需要传入接口和只能代理接口?

需要接口去通过ProxyGenerator类来生成代理类的字节码,在生成的过程中,需要遍历接口中的方法,包括方法签名、参数类型、返回类型从而生成新的代理类,而代理类也需要继承原始接口的方法,所以接口必须要传

3.3:如果同一个接口创建多次代理会怎么办?

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/134460161610747b2b7b7e91a07b672f.html