美文网首页
【啃啊啃 Spring5 源码】细碎三:AOP两种动态代理的区别

【啃啊啃 Spring5 源码】细碎三:AOP两种动态代理的区别

作者: MrDTree | 来源:发表于2018-08-19 13:37 被阅读42次

    spring 中 AOP是基于 “动态代理” 实现,其采用了两种方式:

    1. java代理:采用java内置的代理API实现
    2. cglib代理:采用第三方API实现

    本文主要阐述这两种方式的区别

    动态代理和静态代理

    • 静态代理:编译时将增强代码植入class文件,因为是编译期进行的增强,所以代码运行时效率比动态代理高。使用Aspect可以实现静态代理。
    • 动态代理:运行时生成代理类并加载,效率比静态代理要低,spring中使用了上文中的两种动态代理的方式来实现代理类的生成。

    JDK 动态代理

    cglib代理类的实现效率是比 jdk代理类实现效率要高,并且更强大的,但spring中更推荐使用java原生的代理方式,原因在于,第三方lib的实现有太多不确定性,使用java原生的代理方式更加有保障和稳定。

    java代理类的实现主要靠Proxy类和InvocationHandler接口来实现。下面直接抛出示例代码demo:

    /**
     * jdk 代理的示例demo
     */
    public class MyJdkProxy{
    
        /**
         * 接口
         */
        public interface IHello{
            void sayHello();
        }
    
        /**
         * IHello接口实现类
         */
        static class Hello implements IHello{
            public void sayHello() {
                System.out.println("Hello world!!");
            }
        }
    
        /**
         * 自定义InvocationHandler
         */
        static  class HWInvocationHandler implements InvocationHandler {
            //目标对象
            private Object target;
            public HWInvocationHandler(Object target){
                this.target = target;
            }
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("------插入前置通知代码-------------");
                //执行相应的目标方法
                Object rs = method.invoke(target,args);
                System.out.println("------插入后置处理代码-------------");
                return rs;
            }
        }
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            //生成$Proxy0的class文件
            System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    
            /*
             * 方式一:
             */
            //获取动态代理类class并加载
            Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class);
            //获得代理类的构造函数,并传入参数类型InvocationHandler.class
            Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
            //通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入
            IHello iHello = (IHello) constructor.newInstance(new HWInvocationHandler(new Hello()));
    
            /*
             * 方式二:可看成方式一的语法糖
             */
    //        IHello  iHello = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(),  //加载接口的类加载器
    //                new Class[]{IHello.class},      //一组接口
    //                new HWInvocationHandler(new Hello())); //自定义的InvocationHandler
    
    
            //通过代理对象调用目标方法
            iHello.sayHello();
        }
    }
    

    代理类的class是在Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class)这个方法执行后被生成,class路径在项目根目录的com\sun\proxy中:

    我们反编译下这个代理类class:

    public final class $Proxy0
      extends Proxy
      implements MyJdkProxy.IHello
    {
      private static Method m1;
      private static Method m3;
      private static Method m2;
      private static Method m0;
      
      public $Proxy0(InvocationHandler paramInvocationHandler)
      {
        super(paramInvocationHandler);
      }
      
      public final boolean equals(Object paramObject)
      {
        try
        {
          return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
      
      public final void sayHello()
      {
        try
        {
          this.h.invoke(this, m3, null);
          return;
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
      
      public final String toString()
      {
        try
        {
          return (String)this.h.invoke(this, m2, null);
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
      
      public final int hashCode()
      {
        try
        {
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
      
      static
      {
        try
        {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m3 = Class.forName("test.wsz.spring.aop.MyJdkProxy$IHello").getMethod("sayHello", new Class[0]);
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException)
        {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException)
        {
          throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
      }
    }
    

    从代理类class可以看出,java生成的代理类实现了 被代理对象的接口IHello,同时继承了 Proxy

    这个地方解决了我们的一个疑惑:spring为什么要采用两种动态代理的实现方式?

    原因就在于,JDK的动态代理只能针对被接口修饰的类,因为代理类$Proxy0必须继承Proxy,Java中的类又是单继承的,所以只能规定委托类Hello必须有实现接口IHello,从而使生成的代理类$Proxy0能够重写委托了中的方法sayHello()

    这里梳理下java中代理类的生成步骤

    1. 通过Proxy.getProxyClass()方法生成代理类的class文件并加载。(具体生成步骤见 源码中ProxyClassFactory.apply()
    2. 获取这个class的构造器,传入代理类的逻辑实现类InvocationHandler作为构造函数参数,实例化class获得代理类对象。
    3. 调用代理类对象的对应方法

    具体源码就不分析了,可看 深度剖析JDK动态代理机制

    CGLIB 动态代理

    不多说,直接上demo代码:

    /**
     * cglib 代理的示例demo
     */
    public class MyCglibProxy {
    
    
        /**
         * Hello类
         */
        static class Hello{
            public void sayHello() {
                System.out.println("Hello world!!");
            }
        }
    
        static class HelloMethodInterceptor implements MethodInterceptor {
    
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("------插入前置通知代码-------------");
                //执行相应的目标方法
                Object rs = methodProxy.invokeSuper(o,objects);
                System.out.println("------插入后置处理代码-------------");
    
                return rs;
            }
        }
    
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            //代理类基础自 被代理类Hello
            enhancer.setSuperclass(Hello.class);
            //设置回调类,代理逻辑在回调类中
            enhancer.setCallback(new HelloMethodInterceptor());
    
            //生成代理类
            Hello hello = (Hello) enhancer.create();
            hello.sayHello();
        }
    
    }
    

    生成的代理类class:

    public class MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc
      extends MyCglibProxy.Hello
      implements Factory
    {
      private boolean CGLIB$BOUND;
      public static Object CGLIB$FACTORY_DATA;
      private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
      private static final Callback[] CGLIB$STATIC_CALLBACKS;
      private MethodInterceptor CGLIB$CALLBACK_0;
      private static Object CGLIB$CALLBACK_FILTER;
      private static final Method CGLIB$sayHello$0$Method;
      private static final MethodProxy CGLIB$sayHello$0$Proxy;
      private static final Object[] CGLIB$emptyArgs;
      private static final Method CGLIB$equals$1$Method;
      private static final MethodProxy CGLIB$equals$1$Proxy;
      private static final Method CGLIB$toString$2$Method;
      private static final MethodProxy CGLIB$toString$2$Proxy;
      private static final Method CGLIB$hashCode$3$Method;
      private static final MethodProxy CGLIB$hashCode$3$Proxy;
      private static final Method CGLIB$clone$4$Method;
      private static final MethodProxy CGLIB$clone$4$Proxy;
      
      /* Error */
      static void CGLIB$STATICHOOK1()
      {
      
      }
      
      final void CGLIB$sayHello$0()
      {
        super.sayHello();
      }
      
      public final void sayHello()
      {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null)
        {
          tmp4_1;
          CGLIB$BIND_CALLBACKS(this);
        }
        if (this.CGLIB$CALLBACK_0 != null) {
          return;
        }
        super.sayHello();
      }
      
      final boolean CGLIB$equals$1(Object paramObject)
      {
        return super.equals(paramObject);
      }
      
      public final boolean equals(Object paramObject)
      {
        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)
        {
          Object tmp41_36 = tmp17_14.intercept(this, CGLIB$equals$1$Method, new Object[] { paramObject }, CGLIB$equals$1$Proxy);
          tmp41_36;
          return tmp41_36 == null ? false : ((Boolean)tmp41_36).booleanValue();
        }
        return super.equals(paramObject);
      }
      
      final String CGLIB$toString$2()
      {
        return super.toString();
      }
      
      public final String toString()
      {
        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$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy);
        }
        return super.toString();
      }
      
      final int CGLIB$hashCode$3()
      {
        return super.hashCode();
      }
      
      public final int hashCode()
      {
        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)
        {
          Object tmp36_31 = tmp17_14.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
          tmp36_31;
          return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();
        }
        return super.hashCode();
      }
      
      final Object CGLIB$clone$4()
        throws CloneNotSupportedException
      {
        return super.clone();
      }
      
      protected final Object clone()
        throws CloneNotSupportedException
      {
        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 tmp17_14.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy);
        }
        return super.clone();
      }
      
      /* Error */
      public static MethodProxy CGLIB$findMethodProxy(net.sf.cglib.core.Signature arg0)
      {
        // Byte code:
        //   0: aload_0
        //   1: invokevirtual 119   java/lang/Object:toString   ()Ljava/lang/String;
        //   4: dup
        //   5: invokevirtual 120   java/lang/Object:hashCode   ()I
        //   8: lookupswitch    default:+112->120, -508378822:+52->60, 1535311470:+64->72, 1826985398:+76->84, 1913648695:+88->96, 1984935277:+100->108
        //   60: ldc 122
        //   62: invokevirtual 123  java/lang/Object:equals (Ljava/lang/Object;)Z
        //   65: ifeq +56 -> 121
        //   68: getstatic 116  test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$clone$4$Proxy   Lnet/sf/cglib/proxy/MethodProxy;
        //   71: areturn
        //   72: ldc 125
        //   74: invokevirtual 123  java/lang/Object:equals (Ljava/lang/Object;)Z
        //   77: ifeq +44 -> 121
        //   80: getstatic 49   test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$sayHello$0$Proxy    Lnet/sf/cglib/proxy/MethodProxy;
        //   83: areturn
        //   84: ldc 127
        //   86: invokevirtual 123  java/lang/Object:equals (Ljava/lang/Object;)Z
        //   89: ifeq +32 -> 121
        //   92: getstatic 68   test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$equals$1$Proxy  Lnet/sf/cglib/proxy/MethodProxy;
        //   95: areturn
        //   96: ldc -127
        //   98: invokevirtual 123  java/lang/Object:equals (Ljava/lang/Object;)Z
        //   101: ifeq +20 -> 121
        //   104: getstatic 85  test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$toString$2$Proxy    Lnet/sf/cglib/proxy/MethodProxy;
        //   107: areturn
        //   108: ldc -125
        //   110: invokevirtual 123 java/lang/Object:equals (Ljava/lang/Object;)Z
        //   113: ifeq +8 -> 121
        //   116: getstatic 98  test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$hashCode$3$Proxy    Lnet/sf/cglib/proxy/MethodProxy;
        //   119: areturn
        //   120: pop
        //   121: aconst_null
        //   122: areturn
      }
      
      public MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc()
      {
        CGLIB$BIND_CALLBACKS(this);
      }
      
      public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] paramArrayOfCallback)
      {
        CGLIB$THREAD_CALLBACKS.set(paramArrayOfCallback);
      }
      
      public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] paramArrayOfCallback)
      {
        CGLIB$STATIC_CALLBACKS = paramArrayOfCallback;
      }
      
      private static final void CGLIB$BIND_CALLBACKS(Object paramObject)
      {
        1716bafc local1716bafc = (1716bafc)paramObject;
        if (!local1716bafc.CGLIB$BOUND)
        {
          local1716bafc.CGLIB$BOUND = true;
          Object tmp23_20 = CGLIB$THREAD_CALLBACKS.get();
          if (tmp23_20 == null)
          {
            tmp23_20;
            CGLIB$STATIC_CALLBACKS;
          }
          local1716bafc.CGLIB$CALLBACK_0 = (tmp31_28 == null ? tmp31_28 : (MethodInterceptor)((Callback[])tmp23_20)[0]);
        }
      }
      
      public Object newInstance(Callback[] paramArrayOfCallback)
      {
        CGLIB$SET_THREAD_CALLBACKS(paramArrayOfCallback);
        CGLIB$SET_THREAD_CALLBACKS(null);
        return new 1716bafc();
      }
      
      public Object newInstance(Callback paramCallback)
      {
        CGLIB$SET_THREAD_CALLBACKS(new Callback[] { paramCallback });
        CGLIB$SET_THREAD_CALLBACKS(null);
        return new 1716bafc();
      }
      
      /* Error */
      public Object newInstance(Class[] arg1, Object[] arg2, Callback[] arg3)
      {
        // Byte code:
        //   0: aload_3
        //   1: invokestatic 195    test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$SET_THREAD_CALLBACKS    ([Lnet/sf/cglib/proxy/Callback;)V
        //   4: new 2   test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc
        //   7: dup
        //   8: aload_1
        //   9: dup
        //   10: arraylength
        //   11: tableswitch    default:+24->35, 0:+17->28
        //   28: pop
        //   29: invokespecial 196  test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:<init>    ()V
        //   32: goto +17 -> 49
        //   35: goto +3 -> 38
        //   38: pop
        //   39: new 202    java/lang/IllegalArgumentException
        //   42: dup
        //   43: ldc -52
        //   45: invokespecial 207  java/lang/IllegalArgumentException:<init>   (Ljava/lang/String;)V
        //   48: athrow
        //   49: aconst_null
        //   50: invokestatic 195   test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$SET_THREAD_CALLBACKS    ([Lnet/sf/cglib/proxy/Callback;)V
        //   53: areturn
      }
      
      public Callback getCallback(int paramInt)
      {
        CGLIB$BIND_CALLBACKS(this);
        switch (paramInt)
        {
        case 0: 
          break;
        }
        return null;
      }
      
      public void setCallback(int paramInt, Callback paramCallback)
      {
        switch (paramInt)
        {
        case 0: 
          this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramCallback);
          break;
        }
      }
      
      public Callback[] getCallbacks()
      {
        CGLIB$BIND_CALLBACKS(this);
        return new Callback[] { this.CGLIB$CALLBACK_0 };
      }
      
      public void setCallbacks(Callback[] paramArrayOfCallback)
      {
        this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramArrayOfCallback[0]);
      }
      
      static {}
    }
    

    从反编译的class文件可看出,cglib生成的代理类是直接继承被代理类Hello,代理的具体逻辑是通过回调类HelloMethodInterceptor实现。

    这里不详细分析cglib的源码,可看:深入理解CGLIB动态代理机制

    两种动态代理方式的区别

    1. cglib底层运用了asm这个非常强大的Java字节码生成框架来生成class, 比jdk代理效率要高
    2. jdk代理只能代理被接口修饰的类,而cglib没有这个限制(原因上面已经分析,jdk是动态生成委托类的接口的实现类,cglib是动态生成委托类的子类)

    最后补充下:spring中,如果bean实现了接口,则会使用jdk代理方式,否则采用cglib代理方式。也可通过<aop:aspectj-autoproxy proxy-target-class="true"/>配置来强制使用cglib代理方式

    相关文章

      网友评论

          本文标题:【啃啊啃 Spring5 源码】细碎三:AOP两种动态代理的区别

          本文链接:https://www.haomeiwen.com/subject/ttribftx.html