ProxyGenerator-代理类生成器

ProxyGenerator是JDK-sun包下提供的用于生成动态代理类信息的类,其唯一向外透出的是其静态方法-generateProxyClass(…)。

public class ProxyGenerator {
...
}

学习本篇文章,就是想学习ProxyGenerator如何生成代理类信息的过程。

一、唯一入口-公开静态方法

ProxyGenerator仅提供了一个公开静态方法-方法名为generateProxyClass。从方法入参看,创建代理类信息需传入的参数包括代理类全限定名、代理类实现的接口数组,访问权限标识。实际上,generateProxyClass还有一个重载方法,默认访问权限标识为:public final

public static byte[] generateProxyClass(final String name,
                                            Class<?>[] interfaces,
                                            int accessFlags){
    // 根据必须参数实例化ProxyGenerator类
    ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
    // ProxyGenerator对象生成class文件
    final byte[] classFile = gen.generateClassFile();

	// 是否要保存生成的class文件
    if (saveGeneratedFiles) {
        java.security.AccessController.doPrivileged(
        new java.security.PrivilegedAction<Void>() {
            public Void run() {
                try {
                    int i = name.lastIndexOf('.');
                    Path path;
                    if (i > 0) {
                        Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
                        Files.createDirectories(dir);
                        path = dir.resolve(name.substring(i+1, name.length()) + ".class");
                    } else {
                        path = Paths.get(name + ".class");
                    }
                    Files.write(path, classFile);
                    return null;
                } catch (IOException e) {
                    throw new InternalError(
                        "I/O exception saving generated file: " + e);
                }
            }
        });
    }

    return classFile;
}
  • ProxyGenerator就只提供了一个私有构造方法,内部使用
  • saveGeneratedFiles:根据"sun.misc.ProxyGenerator.saveGeneratedFiles"系统属性值来选择是否保存代理class文件,默认不保存。
  • 保存文件相关日后可以总结下。
    所以,其实重点就在ProxyGenerator对象的generateClassFile()方法。

二、生成代理类流程

下为ProxyGenerator#generateClassFile()生成具体代理类的代码及注释:

private byte[] generateClassFile() {

    /* ============================================================
     * 第一步:组装所有的代理方法, 根据代理签名为Key缓存在proxyMethods属性中。
     */
    /*
     * 1.1 向代理类中增加Object类的必须重写的方法。
     * 这里增加的方法比较早,因此会优先于实现接口的重复方法。
     */
    addProxyMethod(hashCodeMethod, Object.class);
    addProxyMethod(equalsMethod, Object.class);
    addProxyMethod(toStringMethod, Object.class);

    /*
     * 1.2 将接口中的方法信息添加到代理类中。
     * 注意:前面的方法优先级高于后面接口重复方法。
     */
    for (Class<?> intf : interfaces) {
        for (Method m : intf.getMethods()) {
            addProxyMethod(m, intf);
        }
    }

    /*
     * 1.3 校验多个相同方法签名的方法返回值是否兼容
     */
    for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
        checkReturnTypes(sigmethods);
    }

    /* ============================================================
     * 第二步:组装代理类所有的属性和方法信息,包括构造方法、重写方法、静态初始化方法
     */
    try {
    	// 2.1 增加构造方法
        methods.add(generateConstructor());

        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            for (ProxyMethod pm : sigmethods) {

                // add static field for method's Method object
                // 2.2 组装代理类所有的属性-指向方法的属性
                fields.add(new FieldInfo(pm.methodFieldName,
                    "Ljava/lang/reflect/Method;",
                     ACC_PRIVATE | ACC_STATIC));

                // generate code for proxy method and add it
                // 2.3 组装代理类所有的重写方法
                methods.add(pm.generateMethod());
            }
        }

		// 2.4 组装代理类的惊天初始化方法
        methods.add(generateStaticInitializer());

    } catch (IOException e) {
        throw new InternalError("unexpected I/O Exception", e);
    }

    if (methods.size() > 65535) {  // 限制方法和属性的个数限制<=65535
        throw new IllegalArgumentException("method limit exceeded");
    }
    if (fields.size() > 65535) {
        throw new IllegalArgumentException( "field limit exceeded");
    }

    /* ============================================================
     * Step 3: Write the final class file.
     */

    /*
     * Make sure that constant pool indexes are reserved for the
     * following items before starting to write the final class file.
     * 确保常量池中包含类名、父类名、实现的所有接口名
     * 常量池来源:类声明信息、方法信息【前面应添加进去】
     */
    cp.getClass(dotToSlash(className));
    cp.getClass(superclassName);
    for (Class<?> intf: interfaces) {
        cp.getClass(dotToSlash(intf.getName()));
    }

    /*
     * 常量池只为仅读,不再允许写
     */
    cp.setReadOnly();

    ByteArrayOutputStream bout = new ByteArrayOutputStream();
    DataOutputStream dout = new DataOutputStream(bout);

    try {
        /*
         * 按照JVM规范写类文件信息
         */
                                    // u4 magic;
        dout.writeInt(0xCAFEBABE);
                                    // u2 minor_version;
        dout.writeShort(CLASSFILE_MINOR_VERSION);
                                    // u2 major_version;
        dout.writeShort(CLASSFILE_MAJOR_VERSION);

        cp.write(dout);             // (write constant pool)

                                    // u2 access_flags;
        dout.writeShort(accessFlags);
                                    // u2 this_class;
        dout.writeShort(cp.getClass(dotToSlash(className)));
                                    // u2 super_class;
        dout.writeShort(cp.getClass(superclassName));

                                    // u2 interfaces_count;
        dout.writeShort(interfaces.length);
                                    // u2 interfaces[interfaces_count];
        for (Class<?> intf : interfaces) {
            dout.writeShort(cp.getClass(
                dotToSlash(intf.getName())));
        }

                                    // u2 fields_count;
        dout.writeShort(fields.size());
                                    // field_info fields[fields_count];
        for (FieldInfo f : fields) {
            f.write(dout);
        }

                                    // u2 methods_count;
        dout.writeShort(methods.size());
                                    // method_info methods[methods_count];
        for (MethodInfo m : methods) {
            m.write(dout);
        }

                                     // u2 attributes_count;
        dout.writeShort(0); // (no ClassFile attributes for proxy classes)

    } catch (IOException e) {
        throw new InternalError("unexpected I/O Exception", e);
    }

    return bout.toByteArray();
}

Class文件结构:
ProxyGenerator-代理类生成器_第1张图片

你可能感兴趣的:(Java,java,开发语言)