动态代理作为java各框架的基础随处可见,比如spring、mybatis、rpc等都广泛涉及动态代理的应用,spring的aop基于jdk和CGlib两种方式实现动态代理,jdk只能实现基于接口的代理,CGlib通过继承能够实现非final类的代理,javassist通过源码层次来实现代理。
上述结论网上到处可见,我们只讲几个关键点:
1、动态代理是怎么创建的
动态代理的结果是要生成具体的动态代理类,代理类是程序在运行期动态生成的类,这个类和我们平时写代码.java文件里面的类是有区别的,.java文件里面的类需要经过编译成class文件然后jvm类加载器装入内存;运行期生成类肯定没有编译过程了,这时是直接根据class的数据结构生成对应的字节码由jvm类加载器装入的,无论通过jdk也好、还是CGlib、javassist,都是基于class数据结构直接生成字节码,大的结构如下图
2、class数据结构
既然动态代理最终都是通过class数据结构生成字节码的,那么class数据结构长啥样,如下图
3、jdk通过操作字节码生成代理的过程
通过jdk生成动态代理的过程在ProxyGenerator.java里面的private byte[] generateClassFile()方法,和上面class文件的结构保持一致
/* * 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)
注意:为什么jdk只能代理接口,因为上面dout.writeShort(cp.getClass(superclassName));已经制定了父类Proxy,java只能继承一个类;
4、CGlib通过操作字节码生成代理的过程
CGlib是通过ASM操作字节码的,读写的类分别是ClassReader.java和ClassWriter.java
5、javassist通过操作字节码生成代理的过程
javassist后续补充
参考文档:https://tech.meituan.com/2019/09/05/java-bytecode-enhancement.html