设计模式(1-4)-动态代理(ProxyGenerator)

前面讲的都是一些代理类生成的一些准备工作,本节讲讲代理类如何生成出来的一个过程。
java.lang.reflect.Proxy.ProxyClassFactory#apply

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
          ...
       
           byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
          ...
        }

sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[], int)

    public static byte[] generateProxyClass(final String name,
                                            Class<?>[] interfaces,
                                            int accessFlags)
    {
        ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
        
        // !!!! 生成代理类的字节码文件
        final byte[] classFile = gen.generateClassFile();
        
        // 保存代理类的class文件 👇
        // 这样设置就可以保存了,System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        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;
    }

代理类的具体实现就在sun.misc.ProxyGenerator#generateClassFile, 我们来看看它的源码

一、generateClassFile

sun.misc.ProxyGenerator#generateClassFile,根据源码中的注释,我们把源码分成三部

步骤一: 为所有方法组装ProxyMethod对象;检查相同签名(方法名 + 参数列表)的代理方法,其返回类型是否兼容

private byte[] generateClassFile() {
          
        // Obeject的hashcode、equals,toString添加到proxyMethods中,以便代理类中存在这些方法。
        // 它比代理接口中的方法先完成,这样可以保证代理类的方法能重写这三个方法
        addProxyMethod(hashCodeMethod, Object.class);
        addProxyMethod(equalsMethod, Object.class);
        addProxyMethod(toStringMethod, Object.class);

        // 记录来自代理接口的方法
        for (Class<?> intf : interfaces) {
            for (Method m : intf.getMethods()) {
                addProxyMethod(m, intf);
            }
        }

        // 检查相同方法签名的代理方法,返回类型是否兼容
        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            checkReturnTypes(sigmethods);
        }        

        ....

}

步骤二: 为所有fields和methods组装对应的结构

private byte[] generateClassFile() {
      ...
          try {
            // 为代理类生成构造器
            methods.add(generateConstructor());

            for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
                for (ProxyMethod pm : sigmethods) {
                     
                    // 添加方法的Method对象的静态字段, eg, private static Method m1;
                    fields.add(new FieldInfo(pm.methodFieldName,
                        "Ljava/lang/reflect/Method;",
                         ACC_PRIVATE | ACC_STATIC));

                    // 为代理类生成代码并添加它
                    methods.add(pm.generateMethod());
                }
            }
            
            // 为代理类生成初始化方法
            methods.add(generateStaticInitializer());

        } catch (IOException e) {
            throw new InternalError("unexpected I/O Exception", e);
        }
        
        // JVM的规定!
        if (methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        }
        if (fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        }
    ...
}

步骤三: Write the final class file.!

private byte[] generateClassFile() {
      ...

        // 保证下面的这些东西要存在常量池的索引中,在开始写类文件之前
        cp.getClass(dotToSlash(className));
        cp.getClass(superclassName);
        for (Class<?> intf: interfaces) {
            cp.getClass(dotToSlash(intf.getName()));
        }

        /*
         * Disallow new constant pool additions beyond this point, since
         * we are about to write the final constant pool table.
         */
        cp.setReadOnly();

        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        DataOutputStream dout = new DataOutputStream(bout);
        
        try {
            /*
             * Write all the items of the "ClassFile" structure.
             * See JVMS section 4.1.
             */
                                        // 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();

      ...
}

try中的代码,是在写ClassFile(字节码)的结构, 可以参考Chapter 4. The class File Format

上面我们看了代理类的字节码的生成,我们现在再看一看生成的代理类(修改了少量地方),是不是有不一样的感觉了。


// 生成的代理类继承了Proxy
public final class Proxy0 extends Proxy implements UpanSell {
     
    // Method的静态字段
    private static Method m1;
    private static Method m2;
    private static Method m0;
    private static Method m3;
    
    // 构造器
    public Proxy0(InvocationHandler var1)   {
        super(var1);
    }

    // 为代理类生成的方法 👇-------
    @Override
    public final boolean equals(Object var1)   {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    @Override
    public final String toString()  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    @Override
    public final int hashCode()  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    @Override
    public final float sell(int var1)  {
        try {
            return (Float)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m3 = Class.forName("com.ukyu.dynamicproxy.service.UpanSell").getMethod("sell", Integer.TYPE);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

我们将

        UpanSell proxy = (UpanSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
                factory.getClass().getInterfaces(),
                handler
        );

改为 ->

      UpanSell proxy = new Proxy0(handler);

输出的效果是一样的。

二、总结

我们将具体生成代理类的过程走了一遍,结合源码食用更佳

tips: 只能给实现了接口的类,使用JDK动态代理来创建代理类

若想给没有实现接口的类,动态生成代理类,就需要使用CGLIB,可以参考一下CGLib动态代理这篇博客

三、引用

  1. Chapter 4. The class File Format
  2. CGLib动态代理

posted on 2021-11-03 10:06  ukyu  阅读(242)  评论(0编辑  收藏  举报

导航