zoukankan      html  css  js  c++  java
  • 从动态代理到Spring AOP(上)

     一.前言

      虽然平时日常开发很少用到动态代理,但是动态代理在底层框架等有着非常重要的意义。比如Spring AOP使用cglib和JDK动态代理,Hibernate底层使用了javassit和cglib动态代理,Dubbo使用javassist字节码(具体可以看Dubbo SPI)。

      本文主要介绍什么是动态代理及原理,下文将介绍Spring AOP

      我们先思考一个问题:如何统计一个类各个方法的执行时间?可能你心里有好多答案都可以解决问题。

      那么如果是这个项目的多个不同类呢?可能心里也有答案,但是代码改动量不少。那么有什么其他的方法么?

      这时候动态代理就出来了,它可以灵活的在方法、代码点上切入我们想要实现的逻辑。如图示:

    二.体验动态代理

    2.1 JDK动态代理

      在Java的java.lang.reflect包下提供了Proxy类和InvocationHandler接口,通过使用这两个类可以生成JDK动态代理类或者JDK动态代理对象

      JDK动态代理只能针对实现了接口的类进行拓展,我们还是就上面的问题来结局。所以这里我们先创建一个接口,叫Developer(开发),里面有个方法是develop方法。有RookieDeveloper(新生开发)、PrimaryDeveloper(初级开发)和AdvancedDeveloper(高级开发)实现其接口。类图如下:

       我现在对AdvancedDeveloper进行动态代理,先来看一下AdvancedDeveloper的代码:

      接下来请看如何使用Proxy和InvocationHandler生成动态代理的:

     

      运行结果如下:

    2.2 CGLib动态代理

      CGLIB代理的核心是net.sf.cglib.proxy.Enhancer类。我们可以将自定义的net.sf.cglib.proxy.MethodInterceptor实现类来得到强大的代理。代理的所有方法调用都会被分派给net.sf.cglib.proxy.MethodInterceptor的intercept方法。intercept方法然后调用底层对象。

      我们看一下Cglib动态代理的例子,先看下PrimaryDeveloper类:

      再看下CGLib动态代理测试类:

      简而言之,proxy.invoke方法调用的对象不是代理后的子类,proxy.invokeSuper方法调用的对象是代理后的子类(已增强),所以会再走一遍 MyMethodInterceptor的 interceptor方法,如果是个拦截器链条,就会重新在走一次拦截器链;最后看一下执行结果:

    三.动态代理原理

    3.1 JDK动态代理

      我们首先看一下java.lang.reflect.Proxy#newProxyInstance这个方法:

      这里还有一个关键点,在java.lang.reflect.Proxy.ProxyClassFactory#apply方法里,有一段代码生产对应的class字节码文件:

      简单总结一下上面的代码:

    1. 生成一个实现interfaces所有接口且继承Proxy类的代理类
    2. 使用Proxy(InvocationHandler h)构造一个代理类实例
    3. 传入我们定义的InvocationHandler(例子中是匿名内部类),构造器实例化了代理对象

      最后我们看一下生成的类的代码,我使用的是Bytecode Viewer,github地址:https://github.com/Konloch/bytecode-viewer。我们用debug evaluate获取到代理类的class文件,然后用Bytecode Viewer瞅瞅是啥样子:

      生产类的代码出来啦,继承Proxy类,实现Developer接口,调用所有方法都转换成了实际调用InvocationHandler接口的invoke方法:

    3.2 CGLib动态代理

      我们从生成的动态代理类长啥样开始研究。上面的例子,添加System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/miaojiaxing/Downloads");后运行会生成几个.class文件:

    • PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0:CGLib生成的代理类
    • PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0$$FastClassByCGLIB$$aeaf1210:代理类的FastClass
    • PrimaryDeveloper$$FastClassByCGLIB$$de1a7774:被代理类的FastClass(有点绕口)

      首先用反编译工具查看一下PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0这个类的代码:

     1 package com.mjx.java.proxy;
     2 
     3 import java.lang.reflect.Method;
     4 import net.sf.cglib.core.ReflectUtils;
     5 import net.sf.cglib.core.Signature;
     6 import net.sf.cglib.proxy.Callback;
     7 import net.sf.cglib.proxy.Factory;
     8 import net.sf.cglib.proxy.MethodInterceptor;
     9 import net.sf.cglib.proxy.MethodProxy;
    10 
    11 public class PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0 extends PrimaryDeveloper implements Factory {
    12    private boolean CGLIB$BOUND;
    13    public static Object CGLIB$FACTORY_DATA;
    14    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    15    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    16    private MethodInterceptor CGLIB$CALLBACK_0;
    17    private static Object CGLIB$CALLBACK_FILTER;
    18 
    19    // 代理类会获得所有在父类继承来的方法,并且会有MethodProxy与之对应
    20    private static final Method CGLIB$laugh$0$Method;
    21    private static final MethodProxy CGLIB$laugh$0$Proxy;
    22    private static final Object[] CGLIB$emptyArgs;
    23 
    24    // 代理类会获得所有在父类继承来的方法,并且会有MethodProxy与之对应
    25    private static final Method CGLIB$develop$1$Method;
    26    private static final MethodProxy CGLIB$develop$1$Proxy;
    27 
    28    private static final Method CGLIB$say$2$Method;
    29    private static final MethodProxy CGLIB$say$2$Proxy;
    30    private static final Method CGLIB$equals$3$Method;
    31    private static final MethodProxy CGLIB$equals$3$Proxy;
    32    private static final Method CGLIB$toString$4$Method;
    33    private static final MethodProxy CGLIB$toString$4$Proxy;
    34    private static final Method CGLIB$hashCode$5$Method;
    35    private static final MethodProxy CGLIB$hashCode$5$Proxy;
    36    private static final Method CGLIB$clone$6$Method;
    37    private static final MethodProxy CGLIB$clone$6$Proxy;
    38 
    39    static void CGLIB$STATICHOOK1() {
    40       CGLIB$THREAD_CALLBACKS = new ThreadLocal();
    41       CGLIB$emptyArgs = new Object[0];
    42       Class var0 = Class.forName("com.mjx.java.proxy.PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0");
    43       Class var1;
    44       Method[] var10000 = ReflectUtils.findMethods(new String[]{"laugh", "()V", "develop", "()V", "say", "()V"}, (var1 = Class.forName("com.mjx.java.proxy.PrimaryDeveloper")).getDeclaredMethods());
    45       CGLIB$laugh$0$Method = var10000[0];
    46       CGLIB$laugh$0$Proxy = MethodProxy.create(var1, var0, "()V", "laugh", "CGLIB$laugh$0");
    47       CGLIB$develop$1$Method = var10000[1];
    48       CGLIB$develop$1$Proxy = MethodProxy.create(var1, var0, "()V", "develop", "CGLIB$develop$1");
    49       CGLIB$say$2$Method = var10000[2];
    50       CGLIB$say$2$Proxy = MethodProxy.create(var1, var0, "()V", "say", "CGLIB$say$2");
    51       var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
    52       CGLIB$equals$3$Method = var10000[0];
    53       CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
    54       CGLIB$toString$4$Method = var10000[1];
    55       CGLIB$toString$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$4");
    56       CGLIB$hashCode$5$Method = var10000[2];
    57       CGLIB$hashCode$5$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$5");
    58       CGLIB$clone$6$Method = var10000[3];
    59       CGLIB$clone$6$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6");
    60    }
    61 
    62    final void CGLIB$laugh$0() {
    63       super.laugh();
    64    }
    65 
    66    public final void laugh() {
    67       MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
    68       if (var10000 == null) {
    69          CGLIB$BIND_CALLBACKS(this);
    70          var10000 = this.CGLIB$CALLBACK_0;
    71       }
    72 
    73       if (var10000 != null) {
    74          // 拦截器
    75          var10000.intercept(this, CGLIB$laugh$0$Method, CGLIB$emptyArgs, CGLIB$laugh$0$Proxy);
    76       } else {
    77          super.laugh();
    78       }
    79    }
    80 
    81    // methodProxy.invokeSuper会调用
    82    final void CGLIB$develop$1() throws Exception {
    83       super.develop();
    84    }
    85 
    86    public final void develop() throws Exception {
    87       MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
    88       if (var10000 == null) {
    89          CGLIB$BIND_CALLBACKS(this);
    90          var10000 = this.CGLIB$CALLBACK_0;
    91       }
    92 
    93       // 拦截
    94       if (var10000 != null) {
    95          var10000.intercept(this, CGLIB$develop$1$Method, CGLIB$emptyArgs, CGLIB$develop$1$Proxy);
    96       } else {
    97          super.develop();
    98       }
    99    }

      我们可以看到代理类会获得所有在父类继承来的方法,并且会有MethodProxy与之对应,这个非常关键,上面已经用红色标注。

    3.2.1 MethodProxy

      CGLIB$develop$1$Proxy = MethodProxy.create(var1, var0, "()V", "develop", "CGLIB$develop$1");

      我们先看下创建MethodProxy:

     1 public class MethodProxy {
     2     private Signature sig1;
     3     private Signature sig2;
     4     private CreateInfo createInfo;
     5     
     6     private final Object initLock = new Object();
     7     private volatile FastClassInfo fastClassInfo;
     8     
     9     /**
    10      * For internal use by {@link Enhancer} only; see the {@link net.sf.cglib.reflect.FastMethod} class
    11      * for similar functionality.
    12      */
    13     // c1:被代理对象Class
    14     // c2:代理对象Class
    15     // desc:入参类型
    16     // name1:被代理方法名
    17     // name2:代理方法名
    18     public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
    19         MethodProxy proxy = new MethodProxy();
    20         proxy.sig1 = new Signature(name1, desc);
    21         proxy.sig2 = new Signature(name2, desc);
    22         proxy.createInfo = new CreateInfo(c1, c2);
    23         return proxy;
    24     }
    25 
    26     private static class CreateInfo
    27     {
    28         Class c1;
    29         Class c2;
    30         NamingPolicy namingPolicy;
    31         GeneratorStrategy strategy;
    32         boolean attemptLoad;
    33         
    34         public CreateInfo(Class c1, Class c2)
    35         {
    36             this.c1 = c1;
    37             this.c2 = c2;
    38             AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
    39             if (fromEnhancer != null) {
    40                 namingPolicy = fromEnhancer.getNamingPolicy();
    41                 strategy = fromEnhancer.getStrategy();
    42                 attemptLoad = fromEnhancer.getAttemptLoad();
    43             }
    44         }
    45     }
    46 }

      创建代理之后,执行方法这里走到红色的代码:var10000.intercept(this, CGLIB$develop$1$Method, CGLIB$emptyArgs, CGLIB$develop$1$Proxy);走到了我们写的CglibInterceptor类的intercept方法,里面调用了proxy.invokeSuper(obj,args);

     1 public Object invokeSuper(Object obj, Object[] args) throws Throwable {
     2     try {
     3         init();
     4         FastClassInfo fci = fastClassInfo;
     5         // 这里的f2就是PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0$$FastClassByCGLIB$$aeaf1210
     6         return fci.f2.invoke(fci.i2, obj, args);
     7     } catch (InvocationTargetException e) {
     8         throw e.getTargetException();
     9     }
    10 }
    11 
    12 private static class FastClassInfo{
    13     FastClass f1;//被代理类FastClass,这里就是PrimaryDeveloper$$FastClassByCGLIB$$de1a7774
    14     FastClass f2;//代理类FastClass,这里就是PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0$$FastClassByCGLIB$$aeaf1210
    15     int i1;//被代理方法index
    16     int i2;//代理方法index
    17

    3.2.1 FastClass机制  

      Cglib动态代理执行代理方法效率之所以比JDK的高是因为Cglib采用了FastClass机制,它为代理类和被代理类各生成一个Class(就是上面的f1和f2),它会为代理类或被代理类的方法分配一个index(int类型)。这个index是用签名的hashCode来计算出来的Index(下面代码有),FastClass就可以直接定位要调用的方法直接进行调用,那么就省去了反射,所以调用效率比JDK动态代理通过反射调用高。f2我们已经知道是PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0$$FastClassByCGLIB$$aeaf1210,下面我们反编译一下f2看看:

      继续看下f2的invoke方法,直接调用PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0的方法,无需反射。

      最后我们整理一下调用过程,这样比较清晰了吧,如图:

    四.总结

    JDK动态代理:

    • 首先这个类要实现了某接口
    • 其核心就是克隆interfaces的所有接口,继承Proxy类,生成一个心类作为代理类,这个类里有我们定义的实现InvocationHandler接口的类进行代理逻辑处理

    CGLib动态代理:

    • JDK动态代理有个重大缺陷,必须要实现接口才可以使用,而CGLib动态代理只要有个类就行,动态生成子类。如果是private方法,final方法等描述的方法是不能被代理的
    • Cglib动态代理执行代理方法效率之所以比JDK的高是因为Cglib采用了FastClass机制
  • 相关阅读:
    iOS应用内支付(内购)的个人开发过程及坑!
    AJAX实现仿Google Suggest效果
    jquery的show/hide性能测试
    如何做到尽可能不使用庞大的jQuery
    CSS3 transition规范的实际使用经验
    jQuery提升性能技巧及个人总结
    使用CSS3实现超炫的Loading(加载)动画效果
    一个有趣的Ajax Hack示范
    使用ajax技术无刷新动态调用股票信息
    将Asp.Net页面输出到EXCEL里去
  • 原文地址:https://www.cnblogs.com/GrimMjx/p/11194283.html
Copyright © 2011-2022 走看看