动态代理最全详解系列[3]-ProxyGenerator生成代理类的字节码文件源码分析

在之前的生成代理类的源码分析中,我们会看到调用ProxyGenerator.generateProxyClass()生成代理类的字节码文件,下面我们到ProxyGenerator中看看,代理类的字节码到底是怎么生成的。

/**
*ProxyGenerator中的一些字段
*/
//代理类全限定名
private String className;
//代理类要实现的接口
private Class<?>[] interfaces;
//代理类访问标志
private int accessFlags;
//常量池
private ProxyGenerator.ConstantPool cp = new ProxyGenerator.ConstantPool();
//key:接口名,value:ProxyMethod列表,ProxyMethod由接口方法包装而成
private Map<String, List<ProxyGenerator.ProxyMethod>> proxyMethods = new HashMap();

/**
*ProxyGenerator.generateProxyClass()中会调用generateClassFile()生成代理类的字节码文件
*/
private byte[] generateClassFile() {
    //第一步:将所有方法包装成ProxyMethod对象
    //将hashCode、equals、toString方法包装成ProxyMethod对象
    this.addProxyMethod(hashCodeMethod, Object.class);
    this.addProxyMethod(equalsMethod, Object.class);
    this.addProxyMethod(toStringMethod, Object.class);
    //获取代理类接口列表
    Class[] var1 = this.interfaces;
    int var2 = var1.length;

    int var3;
    Class var4;
    //遍历每个接口的每个方法,并包装成ProxyMethod对象
    for(var3 = 0; var3 < var2; ++var3) {
        var4 = var1[var3];
        Method[] var5 = var4.getMethods();
        int var6 = var5.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Method var8 = var5[var7];
            this.addProxyMethod(var8, var4);
        }
    }
    
    Iterator var11 = this.proxyMethods.values().iterator();
    List var12;
    while(var11.hasNext()) {
        var12 = (List)var11.next();
        checkReturnTypes(var12);
    }
    
    //第二步:为代理类组装各种字段信息和方法信息
    Iterator var15;
    try {
        //添加构造器方法,该构造器参数为InvocationHandler类型
        this.methods.add(this.generateConstructor());
        //添加静态字段及代理方法
        var11 = this.proxyMethods.values().iterator();
        while(var11.hasNext()) {
            var12 = (List)var11.next();
            var15 = var12.iterator();
            while(var15.hasNext()) {
                ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                //添加私有的静态字段,字段值为各方法的Method类
                //10 = PRIVATE | STATIC
                this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                //添加代理方法
                this.methods.add(var16.generateMethod());
            }
        }
        //添加静态字段的初始化方法
        this.methods.add(this.generateStaticInitializer());
    } catch (IOException var10) {
        throw new InternalError("unexpected I/O Exception", var10);
    }

    //第三步:写入class文件
    if (this.methods.size() > 65535) {
        throw new IllegalArgumentException("method limit exceeded");
    } else if (this.fields.size() > 65535) {
        throw new IllegalArgumentException("field limit exceeded");
    } else {
        //验证常量池中存在代理类的全限定名
        this.cp.getClass(dotToSlash(this.className));
        //验证常量池中存在代理类父类的全限定名,即Proxy类
        this.cp.getClass("java/lang/reflect/Proxy");
        //验证常量池中存在代理类接口的全限定名
        var1 = this.interfaces;
        var2 = var1.length;
        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            this.cp.getClass(dotToSlash(var4.getName()));
        }
        //写入class文件前,将常量池设为只读,当前常量池中的变量不允许修改
        this.cp.setReadOnly();
        
        ByteArrayOutputStream var13 = new ByteArrayOutputStream();
        DataOutputStream var14 = new DataOutputStream(var13);
        try {
            //每个class文件的前四个字节为魔数,用来确定这个文件是否是一个能被虚拟机接受的class文件,
            //后面再跟两个字节的次版本号和两个字节的主版本号
            
            //写入魔数
            var14.writeInt(-889275714);
            //写入次版本号
            var14.writeShort(0);
            //写入主版本号
            var14.writeShort(49);
            //写入常量池
            this.cp.write(var14);
            //写入访问标志
            var14.writeShort(this.accessFlags);
            //写入代理类类型
            var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
            //写入父类类型
            var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
            //写入接口数量
            var14.writeShort(this.interfaces.length);
            //写入接口类型
            Class[] var17 = this.interfaces;
            int var18 = var17.length;
            for(int var19 = 0; var19 < var18; ++var19) {
                Class var22 = var17[var19];
                var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
            }
            //写入字段数量
            var14.writeShort(this.fields.size());
            //写入字段信息
            var15 = this.fields.iterator();
            while(var15.hasNext()) {
                ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                var20.write(var14);
            }
            //写入方法数量
            var14.writeShort(this.methods.size());
            //写入方法信息
            var15 = this.methods.iterator();
            while(var15.hasNext()) {
                ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                var21.write(var14);
            }
            //写入属性数量,代理类中没有属性
            var14.writeShort(0);
            //转成二进制文件输出
            return var13.toByteArray();
        } catch (IOException var9) {
            throw new InternalError("unexpected I/O Exception", var9);
        }
    }
}

通过分析代理类生成的流程,我们可以看到,generateClassFile()按照.class文件的结构拼接出了代理类的class文件。通常生成.class文件要先写一个.java文件,然后调用javac.exe命令或编译器自动将.java编译为.class,JVM将.class加载到内存后就可以运行了,而generateClassFile()跳过了.java直接拼出了.class。


我们再结合动态代理总结一下代理类class文件的组装过程

1.收集代理类要实现的接口中的所有方法,包装成ProxyMethod对象,并通过Map保存接口名到ProxyMethod对象的映射;

2.添加参数类型为InvocationHandler的代理类构造器方法;

3.遍历Map,生成每个代理方法的Method类,作为静态字段添加到代理类中;

4.遍历Map,完成每个代理方法的实现,添加到代理类中;

5.添加静态初始化方法,将每个代理方法的引用赋值给对应的静态字段;

此时,我们再回头看一下上一篇中生成的$Proxy0代理类,就会看到所有的这些组装信息。
欢迎关注我的公众号【codeZhao】,获取更多技术干货和职业思考。

你可能感兴趣的:(java,设计模式,jdk动态代理,源码)