Spring AOP实际上是基于动态代理实现的,只不过Spring 同时支持JDK Proxy和cglib,下面我们来介绍一下这两种实现动态代理的方式
注:本示例中使用JDK1.8
/** * 在代理的接口调用时的处理器类 */ class MyHandler implements InvocationHandler { private final Object target; public MyHandler(final Object target) { this.target = target; } @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println("-----before----"); final Object o = method.invoke(this.target, args); System.out.println("-----after----"); return o; } } public static void main(final String[] args) { final BizAImpl bizAImpl = new BizAImpl(); final IBizA newBizA = (IBizA) Proxy.newProxyInstance(MyHandler.class.getClassLoader(), bizAImpl.getClass().getInterfaces(), new MyHandler(bizAImpl)); newBizA.doSomething(); } 复制代码
class MyHandler implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // TODO Auto-generated method stub System.out.println("----before----"); proxy.invokeSuper(obj, args); System.out.println("----after----"); return obj; } } public static void main(String[] args) { MyHandler myHandler = new MyHandler(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(BizA.class); enhancer.setCallback(myHandler); BizA bizA = (BizA) enhancer.create(); bizA.doSomething(); } 复制代码
从示例代码中,我们可以得出以下结论
JDK Proxy | Cglib |
---|---|
要求代理类必须实现接口,参见IBizA和BizImpl | 支持类的代理,对接口实现没有要求 |
需要将代理目标对象(bizImpl)传递给InvocationHandler,在写法上不够灵活 | 可以直接根据类(BizA.class)生成目标代理对象 |
实际上,这两种代理的实现方式也大不一样
JDK Proxy是通过复制原有代理类,然后生成一个新类,在生成新类的同时,将方法的调用转给了InvocationHandler,在代理类执行方法时,实际上是调用了InvocationHandler的invoke方法。
我们看下JDK源码,从newProxyInstance方法开始
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) { /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ final Constructor<?> cons = cl.getConstructor(constructorParams); return cons.newInstance(new Object[]{h}); } 复制代码
newProxyInstance方法只干了两件事
那么这里的重点是如果获取代理类呢,我们接着往下看
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory return proxyClassCache.get(loader, interfaces); } 复制代码
优先从缓存中获取,如果缓存中没有,会通过工厂(ProxyClassFactory)生成,缓存部分代码略过,直接看生成代理类的流程
/** * A factory function that generates, defines and returns the proxy class given * the ClassLoader and array of interfaces. */ private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { //校验接口,访问权限,确定package等,省略... /* * Choose a name for the proxy class to generate. */ //生成代理类名字 long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); } } } 复制代码
将核心方法提取出来
//生成字节码数据 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); //这是一个native方法 return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length); 复制代码
进入方法ProxyGenerator.generateProxyClass()
public static byte[] generateProxyClass(final String name, Class<?>[] interfaces, int accessFlags) { ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags); final byte[] classFile = gen.generateClassFile(); //这个标志是系统的一个配置,是否保存生成的文件,可以通过配置项 //sun.misc.ProxyGenerator.saveGeneratedFiles拿到 if (saveGeneratedFiles) { //...保存文件,非讨论重点 } return classFile; } 复制代码
生成类文件的核心方法
private byte[] generateClassFile() { /* ============================================================ * Step 1: Assemble ProxyMethod objects for all methods to * generate proxy dispatching code for. * 第一步:添加代理方法(ProxyMethod) */ //object公用方法 addProxyMethod(hashCodeMethod, Object.class); addProxyMethod(equalsMethod, Object.class); addProxyMethod(toStringMethod, Object.class); //代理实现的接口方法 for (Class<?> intf : interfaces) { for (Method m : intf.getMethods()) { addProxyMethod(m, intf); } } /* ============================================================ * Step 2: Assemble FieldInfo and MethodInfo structs for all of * fields and methods in the class we are generating. * 步骤2:将代理方法(MethodProxy)转换为方法(MethodInfo),MethodInfo实现了写入字节码 */ try { methods.add(generateConstructor()); for (List<ProxyMethod> sigmethods : proxyMethods.values()) { for (ProxyMethod pm : sigmethods) { // add static field for method's Method object fields.add(new FieldInfo(pm.methodFieldName, "Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC)); //代理方法(MethodProxy)转换为方法(MethodInfo) // generate code for proxy method and add it methods.add(pm.generateMethod()); } } methods.add(generateStaticInitializer()); } catch (IOException e) { throw new InternalError("unexpected I/O Exception", e); } /* ============================================================ * Step 3: Write the final class file. * 步骤3:写入类文件 */ ByteArrayOutputStream bout = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(bout); try { /* * Write all the items of the "ClassFile" structure. * See JVMS section 4.1. */ //...依照JVM规范生成字节码,省略 } catch (IOException e) { throw new InternalError("unexpected I/O Exception", e); } return bout.toByteArray(); } 复制代码
再次进入ProxyMethod转换为MethodInfo的方法generateMethod(),这里的内容较多,我们只贴出来关键的一处,将InvocationHandler的调用加入了新方法中
out.writeByte(opc_invokeinterface); out.writeShort(cp.getInterfaceMethodRef( "java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;" + "[Ljava/lang/Object;)Ljava/lang/Object;")); out.writeByte(4); out.writeByte(0); 复制代码
由此我们了解了整个JDK Proxy的执行过程,最终可以从生成的class文件($Proxy0.class)中反编译,反编译结果如下:
package com.sun.proxy; import com.proxy.IBizA; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class Proxy0 extends Proxy implements IBizA { public Proxy0() throws { super(paramInvocationHandler); } public final void doSomething() throws { try { this.h.invoke(this, m3, null); return; } } public final String toString() throws { try { return ((String)this.h.invoke(this, m2, null)); } } } 复制代码
我们从反编译的文件中可以看出来,实际上代理类调用了InvocationHandler的invoke方法
注:系统默认不输出代理class文件,如果要实现新增代理class文件的输出,需要在main方法中加上
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 复制代码
注:Proxy相关的代码可以直接通过jdk源码看到,ProxyGenerator的代码请参考:
github.com/JetBrains/j…
cglib是通过asm库实现了代理class的生成,cglib会生成两个类,一个类是作为代理类使用,一个类是作为方法调用时使用。在调用方法的时候,cglib做了一些优化,没有采用反射调用方法,而是给每个代理方法增加一个索引,然后根据索引找到方法,并直接调用。
注:asm是一个操作Java字节码的库 参考资料: https://www.ibm.com/developerworks/cn/java/j-lo-asm30/index.html https://asm.ow2.io/ 复制代码
我们首先看下cglib是如何生成代理类的,由于cglib的代码较为复杂,我们只贴出来关键部分.
cglib和JDK proxy有一点比较类似,他们都是优先从缓存中获取,如果取不到才会调用生成class的方法,我们直接看AbstractClassGenerator类的generate方法
protected Class generate(ClassLoaderData data) { Class gen; Object save = CURRENT.get(); CURRENT.set(this); try { ClassLoader classLoader = data.getClassLoader(); synchronized (classLoader) { String name = generateClassName(data.getUniqueNamePredicate()); data.reserveName(name); this.setClassName(name); } if (attemptLoad) { try { gen = classLoader.loadClass(getClassName()); return gen; } catch (ClassNotFoundException e) { // ignore } } //关键代码,调用了strategy的generate方法 byte[] b = strategy.generate(this); String className = ClassNameReader.getClassName(new ClassReader(b)); ProtectionDomain protectionDomain = getProtectionDomain(); synchronized (classLoader) { // just in case if (protectionDomain == null) { gen = ReflectUtils.defineClass(className, b, classLoader); } else { gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain); } } return gen; } catch (RuntimeException e) { throw e; } catch (Error e) { throw e; } catch (Exception e) { throw new CodeGenerationException(e); } finally { CURRENT.set(save); } } 复制代码
调用了GeneratorStrategy的generate方法,默认策略是DefaultGeneratorStrategy,我们继续跟踪
public byte[] generate(ClassGenerator cg) throws Exception { DebuggingClassWriter cw = getClassVisitor(); transform(cg).generateClass(cw); return transform(cw.toByteArray()); } protected ClassGenerator transform(ClassGenerator cg) throws Exception { return cg; } 复制代码
默认策略并没有做什么实际的工作,直接调用了ClassGenerator接口的generateClass方法。
我们回到AbstractClassGenerator类,该类并没有定义generateClass方法,但在他的实现类Enhancer中定义了generateClass方法,而实际上也是调用了Enhancer的方法。
public void generateClass(ClassVisitor v) throws Exception { Class sc = (superclass == null) ? Object.class : superclass; // Order is very important: must add superclass, then // its superclass chain, then each interface and // its superinterfaces. List actualMethods = new ArrayList(); List interfaceMethods = new ArrayList(); final Set forcePublic = new HashSet(); getMethods(sc, interfaces, actualMethods, interfaceMethods, forcePublic); List methods = CollectionUtils.transform(actualMethods, new Transformer() { public Object transform(Object value) { Method method = (Method)value; //... return ReflectUtils.getMethodInfo(method, modifiers); } }); //类的开始 ClassEmitter e = new ClassEmitter(v); e.begin_class(Constants.V1_8, Constants.ACC_PUBLIC, getClassName(), Type.getType(sc), (useFactory ? TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) : TypeUtils.getTypes(interfaces)), Constants.SOURCE_FILE); //声明属性 e.declare_field(Constants.ACC_PRIVATE, BOUND_FIELD, Type.BOOLEAN_TYPE, null); e.declare_field(Constants.ACC_PUBLIC | Constants.ACC_STATIC, FACTORY_DATA_FIELD, OBJECT_TYPE, null); e.declare_field(Constants.PRIVATE_FINAL_STATIC, THREAD_CALLBACKS_FIELD, THREAD_LOCAL, null); e.declare_field(Constants.PRIVATE_FINAL_STATIC, STATIC_CALLBACKS_FIELD, CALLBACK_ARRAY, null); // This is declared private to avoid "public field" pollution e.declare_field(Constants.ACC_PRIVATE | Constants.ACC_STATIC, CALLBACK_FILTER_FIELD, OBJECT_TYPE, null); //构造函数 emitDefaultConstructor(e); //实现回调 emitSetThreadCallbacks(e); emitSetStaticCallbacks(e); emitBindCallbacks(e); //... //类的结束 e.end_class(); } 复制代码
本段代码的核心是使用ClassEmitter(对asm ClassVisitor的封装)构造出来一个类,这个类实现了代理目标类(BizA)的方法,然后也增加了方法拦截器的调用,从反编译代码中我们可以看出来
public final String doSomething() { MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0; if (tmp4_1 == null) { tmp4_1; CGLIB$BIND_CALLBACKS(this); } MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0; if (tmp17_14 != null) return ((String)tmp17_14.intercept(this, CGLIB$doSomething$0$Method, CGLIB$emptyArgs, CGLIB$doSomething$0$Proxy)); return super.doSomething(); } 复制代码
注意这一段代码
tmp17_14.intercept(this, CGLIB$doSomething$0$Method, CGLIB$emptyArgs, CGLIB$doSomething$0$Proxy)); 复制代码
实现了方法拦截器(MethodInterceptor)的调用。
在生成代理类的同时,cglib同时生成了另外一个类FastClass,这个会在下面详细介绍
从cglib方式的代码示例中,我们看到,实际上在intercept方法中,我们是这么调用的
proxy.invokeSuper(obj, args); 复制代码
我们进一步看看这个方法做了什么
public Object invokeSuper(Object obj, Object[] args) throws Throwable { try { init(); FastClassInfo fci = fastClassInfo; return fci.f2.invoke(fci.i2, obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } } 复制代码
忽略掉init,实际上是调用了FastClass的invoke方法
/** * Invoke the method with the specified index. * @see getIndex(name, Class[]) * @param index the method index * @param obj the object the underlying method is invoked from * @param args the arguments used for the method call * @throws java.lang.reflect.InvocationTargetException if the underlying method throws an exception */ abstract public Object invoke(int index, Object obj, Object[] args) throws InvocationTargetException; 复制代码
这是一个抽象方法,方法的实现是在动态生成的FastClass子类中(稍后会贴出反编译字节码)。从方法描述中,我们能看出来,FastClass根据不同的索引调用不同的方法( 注意,这里是直接调用而不是反射 )。我们看下反编译代码
public Object invoke(, Object paramObject, Object[] paramArrayOfObject) throws InvocationTargetException { // Byte code: // 0: aload_2 // 1: checkcast 133 com/cglib/BizA$$EnhancerByCGLIB$$eca2fdc // 4: iload_1 // 5: tableswitch default:+304 -> 309, 0:+99->104, 1:+114->119.... //.... // 198: invokevirtual 177 com/cglib/BizA$$EnhancerByCGLIB$$eca2fdc:doSomething ()Ljava/lang/String; //... // 272: aconst_null // 273: areturn // 274: invokevirtual 199 com/cglib/BizA$$EnhancerByCGLIB$$eca2fdc:CGLIB$toString$2 ()Ljava/lang/String; // 277: areturn // 278: invokevirtual 201 com/cglib/BizA$$EnhancerByCGLIB$$eca2fdc:CGLIB$clone$4 ()Ljava/lang/Object; // 281: areturn // 330: athrow // // Exception table: // from to target type // 5 312 312 java/lang/Throwable } 复制代码
我们看到,这里使用了tableswitch,可以认为是switch语句。根据不同的值调用不同的方法。从198行中我们可以看到调用了doSomething方法。
这是cglib的一大优势,通过创建索引,减少了反射的过程,也提升了动态代理的性能。另外cglib代码中大量使用了缓存,懒加载等机制,这对提升性能也有不少帮助。
cglib的代码比较复杂,由于文章篇幅原因,我们只是从动态代理实现的角度简单说明。cglib代码质量非常高,其中运用了大量的设计模式,对于线程安全,缓存,懒加载等都有涉及,有兴趣的同学可以直接阅读下源码,我在附录中贴出了源码的链接。
注:通过设置类输出目录,可以将cglib生成的类输出 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C://classes"); 复制代码