美文网首页SpringFramework
Spring AOP (三) CGLIB 动态代理

Spring AOP (三) CGLIB 动态代理

作者: 蓝笔头 | 来源:发表于2019-05-06 17:21 被阅读34次

    在深入理解之前,我们先来看一个简单的例子。

    首先,导入 CGLIB 的 Maven 依赖。

    <!-- https://mvnrepository.com/artifact/cglib/cglib -->
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.2.11</version>
    </dependency>
    

    Spring AOPorg.springframework.cglib 包中包含了 CGLIB 的相关代码(和 CGLIB Maven 依赖中代码的一样,版本可能不同),所以也可以选择导入 Spring AOP 的 Maven 依赖。

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    

    这里选择导入 CGLIB 依赖(因为 Spring 框架中的 cglib 不能下载源码)。

    然后,定义一个 Service 类,其有两个方法并且其中一个方法用 final 来修饰。

    public class Service {
    
        /**
         *  final 方法不能被子类覆盖
         */
        public final void finalMethod() {
            System.out.println("Service.finalMethod 执行了");
        }
    
        public void publicMethod() {
            System.out.println("Service.publicMethod 执行了");
        }
    }
    

    接下来,定义一个 MethodInterceptor 的实现类。

    public class CglibDynamicProxy implements MethodInterceptor {
    
        /**
         * 目标对象(也被称为被代理对象)
         */
        private Object target;
    
        public CglibDynamicProxy(Object target) {
            this.target = target;
        }
        /**
         *
         * @param obj       CGLIB 生成的代理对象
         * @param method    被代理对象方法
         * @param args      方法入参
         * @param proxy     方法代理
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("CglibDynamicProxy intercept 方法执行前-------------------------------");
    
            System.out.println("obj = " + obj.getClass());
            System.out.println("method = " + method);
            System.out.println("proxy = " + proxy);
    
            Object object = proxy.invoke(target, args);
            System.out.println("CglibDynamicProxy intercept 方法执行后-------------------------------");
            return object;
        }
    
        /**
         * 获取被代理接口实例对象
         * 
         * 通过 enhancer.create 可以获得一个代理对象,它继承了 target.getClass() 类
         *
         * @param <T>
         * @return
         */
        public <T> T getProxy() {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target.getClass());
            enhancer.setCallback(this);
            return (T) enhancer.create();
        }
    }
    

    通过 Client 组合上述代码,进行测试。

    public class Client {
        public static void main(String[] args) {
            // 1. 构造目标对象
            Service target = new Service();
    
            // 2. 根据目标对象生成代理对象
            CglibDynamicProxy proxy = new CglibDynamicProxy(target);
    
            // 获取 CGLIB 代理类
            Service proxyObject = proxy.getProxy();
    
            // 调用代理对象的方法
            proxyObject.finalMethod();
            proxyObject.publicMethod();
        }
    }
    

    测试结果如下所示:

    测试结果.jpg

    源码解析

    上文中的是通过 enhancer.create 方法调用获取的代理对象,以此为入口深入探究一下 CGLIB 动态代理的实现原理。

    // 生成代理类名称是用到了 SOURCE 字段
    private static final Source SOURCE = new Source(Enhancer.class.getName());
    
    public Enhancer() {
        super(SOURCE);
    }
    
    public Object create() {
      return createHelper();
    }
    
    private Object createHelper() {
      Object key = ...;
      Object result = super.create(key);
      return result;
    }
    
    private static volatile Map<ClassLoader, ClassLoaderData> CACHE = new WeakHashMap<ClassLoader, ClassLoaderData>();
    
    protected Object create(Object key) {
        Map<ClassLoader, ClassLoaderData> cache = CACHE;
        ClassLoaderData data = cache.get(loader);
        
        // 也就是 ClassLoaderData 的 get 方法
        Object obj = data.get(this, getUseCache());
        if (obj instanceof Class) {
          return firstInstance((Class) obj);
        }
        return nextInstance(obj);
    }
    

    接下来我们关注 ClassLoaderDataget 方法的逻辑。

    public Object get(AbstractClassGenerator gen, boolean useCache) {
      if (!useCache) {
        return gen.generate(ClassLoaderData.this);
      } else {
        Object cachedValue = generatedClasses.get(gen);
        return gen.unwrapCachedValue(cachedValue);
      }
    }
    

    AbstractClassGenerator 类中的 generate 方法逻辑如下所示:

    // 代理类字节码的默认生成策略
    private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE;
    
    // 代理类的默认命名策略
    private NamingPolicy namingPolicy = DefaultNamingPolicy.INSTANCE;
    
    protected Class generate(ClassLoaderData data) {
      Class gen;
      try {
        ClassLoader classLoader = data.getClassLoader();
        if (classLoader == null) {
          throw new IllegalStateException("ClassLoader is null while trying to define class " +
              getClassName() + ". It seems that the loader has been expired from a weak reference somehow. " +
              "Please file an issue at cglib's issue tracker.");
        }
        synchronized (classLoader) {
          // 1. 生成代理类的类名
          String name = generateClassName(data.getUniqueNamePredicate());              
          data.reserveName(name);
          this.setClassName(name);
        }
        // 2. 生成代理类字节码的二进制数组
        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;
    }
    
    private String generateClassName(Predicate nameTestPredicate) {
        return namingPolicy.getClassName(namePrefix, source.name, key, nameTestPredicate);
    }
    

    CGLIB 通过 DefaultNamingPolicy 类中的 getClassName 方法获取到代理类的名称,逻辑如下所示。

    /**
     *  为代理类选择一个类名
     * @param prefix  被代理类的全路径名称,比如这里的 com.wilimm.proxy.cglib.Service
     * @param source  生成代理类的全限定名(如 "net.sf.cglib.Enhance", "net.sf.cglib.reflect.FastClass")
     * @param key    表示参数状态的关键对象; 要使缓存正常工作,相等的键应该生成相同的生成类名。默认策略将 key.hashCode() 合并到类名中。
     * @param names  如果给定的类名已经在同一个 ClassLoader 中使用,则返回 true 的谓词。
     * @return 全限定的代理类类名
     */
    public String getClassName(String prefix, String source, Object key, Predicate names) {
      String base =
        prefix + "$$" + 
        source.substring(source.lastIndexOf('.') + 1) +
        getTag() + "$$" +
        Integer.toHexString(STRESS_HASH_CODE ? 0 : key.hashCode());
      String attempt = base;
      int index = 2;
      
      // 检测生成的代理类名是否已经生成,如果已经生成,则需要添加后缀
      while (names.evaluate(attempt))
        attempt = base + "_" + index++;
      return attempt;
    }
    
    protected String getTag() {
        return "ByCGLIB";
    }
    

    我们简单看一下 DefaultGeneratorStrategy 类中的 generate 方法,这是真正生成代理类的地方。

    public byte[] generate(ClassGenerator cg) throws Exception {
      DebuggingClassWriter cw = getClassVisitor();
      // 生成代理类的地方,代码逻辑过于复杂,暂时忽略
      transform(cg).generateClass(cw);
      return transform(cw.toByteArray());
    }
    

    最后,我们重点关注下 DebuggingClassWriter 类中的 toByteArray 方法,在这个方法中我们可以看到如何保存 CGLIB 代理类 Class 到文件中

    public static final String DEBUG_LOCATION_PROPERTY = "cglib.debugLocation";
        
    private static String debugLocation;
    
    static {
      debugLocation = System.getProperty(DEBUG_LOCATION_PROPERTY);
      if (debugLocation != null) {
        System.err.println("CGLIB debugging enabled, writing to '" + debugLocation + "'");
      }
    }
    
    public byte[] toByteArray() {
      return (byte[]) java.security.AccessController.doPrivileged(
      new java.security.PrivilegedAction() {
        public Object run() {
          byte[] b = ((ClassWriter) DebuggingClassWriter.super.cv).toByteArray();
          
          // 如果 DebuggingClassWriter.DEBUG_LOCATION_PROPERTY 系统属性被设置,则输出代理类到指定目录
          if (debugLocation != null) {
            String dirs = className.replace('.', File.separatorChar);
            try {
              new File(debugLocation + File.separatorChar + dirs).getParentFile().mkdirs();
              
              File file = new File(new File(debugLocation), dirs + ".class");
              OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
              try {
                out.write(b);
              } finally {
                out.close();
              }
            } catch (Exception e) {
              throw new CodeGenerationException(e);
            }
          }
          return b;
         }  
        });
      }
    }
    

    CGLIB 生成的代理类剖析

    由上文可知,把 DebuggingClassWriter.DEBUG_LOCATION_PROPERTY(也就是 cglib.debugLocation)系统属性设置为当前项目的根目录,即可保存 CGLIB 生成的代理类到当前项目根目录下。

    // 获取当前项目的根目录
    String userDir = System.getProperty("user.dir");
    //System.setProperty("cglib.debugLocation", userDir);
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, userDir);
    

    运行前文中 Clientmain 方法,即可在当前项目根目录下看到 CGLIB 生成的代理类 Class 文件,如下图所示。

    CGLIB 代理类.jpg

    Service$$EnhancerByCGLIB$$bdabbd96 就是 CGLIB 生成的代理类,它继承了 Service 类。

    我们来简单分析下 Service$$EnhancerByCGLIB$$bdabbd96 中的代码结构。

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    package com.wilimm.proxy.cglib;
    
    import java.lang.reflect.Method;
    import net.sf.cglib.core.ReflectUtils;
    import net.sf.cglib.core.Signature;
    import net.sf.cglib.proxy.Callback;
    import net.sf.cglib.proxy.Factory;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class Service$$EnhancerByCGLIB$$bdabbd96 extends Service implements Factory {
        private MethodInterceptor CGLIB$CALLBACK_0; // 拦截器
        private static final Method CGLIB$publicMethod$0$Method; // 被代理方法
        private static final MethodProxy CGLIB$publicMethod$0$Proxy; // 代理方法
    
        static void CGLIB$STATICHOOK1() {
            // 代理类
            Class var0 = Class.forName("com.wilimm.proxy.cglib.Service$$EnhancerByCGLIB$$bdabbd96");
            // 被代理类
            Class var1;
            CGLIB$publicMethod$0$Method = ReflectUtils.findMethods(new String[]{"publicMethod", "()V"}, (var1 = Class.forName("com.wilimm.proxy.cglib.Service")).getDeclaredMethods())[0];
            CGLIB$publicMethod$0$Proxy = MethodProxy.create(var1, var0, "()V", "publicMethod", "CGLIB$publicMethod$0");
        }
        
        // 代理方法(methodProxy.invokeSuper会调用)
        final void CGLIB$publicMethod$0() {
            super.publicMethod();
        }
    
        // 被代理方法(methodProxy.invoke 会调用,这就是为什么在拦截器中调用 methodProxy.invoke 会死循环,一直在调用拦截器)
        public final void publicMethod() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (var10000 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                // 调用拦截器
                var10000.intercept(this, CGLIB$publicMethod$0$Method, CGLIB$emptyArgs, CGLIB$publicMethod$0$Proxy);
            } else {
                super.publicMethod();
            }
        }
        
        static {
            CGLIB$STATICHOOK1();
        }
    }
    

    MethodProxy 类非常关键,我们分析一下它的内部逻辑。

    public class MethodProxy {
        private Signature sig1;
        private Signature sig2;
        private CreateInfo createInfo;
        
        private final Object initLock = new Object();
        private volatile FastClassInfo fastClassInfo;
        
        /**
         *
         * @param c1  被代理对象 Class
         * @param c2  CGLIB 代理对象 Class
         * @param desc 入参类型
         * @param name1 有拦截器逻辑的方法名 publicMethod
         * @param name2 直接调用父类方法的方法名 CGLIB$publicMethod$0
         * @return
         */
        public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
            MethodProxy proxy = new MethodProxy();
            proxy.sig1 = new Signature(name1, desc);
            proxy.sig2 = new Signature(name2, desc);
            proxy.createInfo = new CreateInfo(c1, c2);
            return proxy;
        }
        
        private void init() {
              CreateInfo ci = createInfo;
              FastClassInfo fci = new FastClassInfo();
              // 1. 生成被代理类 FastClass,在这里是 Service$$FastClassByCGLIB$$fdf36b96
              fci.f1 = helper(ci, ci.c1);
              // 2. 生成代理类 FastClass,在这里是 Service$$EnhancerByCGLIB$$bdabbd96$$FastClassByCGLIB$$a112cdb2
              fci.f2 = helper(ci, ci.c2);
              // 3. 得到被代理类 FastClass 中的 publicMethod 方法签名
              fci.i1 = fci.f1.getIndex(sig1);
              // 4. 得到代理类 FastClass 中的 CGLIB$publicMethod$0 方法签名
              fci.i2 = fci.f2.getIndex(sig2);
              fastClassInfo = fci;
              createInfo = null;
        }
        
        private static class FastClassInfo {
            FastClass f1; //被代理类 FastClass,在这里是 Service$$FastClassByCGLIB$$fdf36b96
            FastClass f2; //代理类 FastClass,在这里是 Service$$EnhancerByCGLIB$$bdabbd96$$FastClassByCGLIB$$a112cdb2
            int i1; // 被代理类 FastClass 中的 publicMethod方法签名(index)
            int i2; // 代理类 FastClass 中的 CGLIB$publicMethod$0 的方法签名
        }
        
        /**
         * 在相同类型的不同对象上调用原始方法。如果传入的是代理对象,则调用的是 CGLIB 代理类重写的方法     
         * @param obj 方法调用的对象,如果使用 MethodInterceptor 的 intercept 方法的第一个参数,将导致递归
         * @param args 参数列表
         */
        public Object invoke(Object obj, Object[] args) throws Throwable {
            try {
                init();
                FastClassInfo fci = fastClassInfo;
                // 调用 Service$$FastClassByCGLIB$$fdf36b96 类中的 publicMethod 方法
                return fci.f1.invoke(fci.i1, obj, args);
            } catch (InvocationTargetException e) {
                throw e.getTargetException();
            } catch (IllegalArgumentException e) {
                if (fastClassInfo.i1 < 0)
                    throw new IllegalArgumentException("Protected method: " + sig1);
                throw e;
            }
        }
    
        /**
         * 在代理对象上调用父类的原始方法
         * @param obj CGLIB 生成的代理对象
         * @param args 参数列表
         */
        public Object invokeSuper(Object obj, Object[] args) throws Throwable {
            try {
                init();
                FastClassInfo fci = fastClassInfo;
                // 调用 Service$$EnhancerByCGLIB$$bdabbd96$$FastClassByCGLIB$$a112cdb2 类中的 CGLIB$publicMethod$0 方法
                return fci.f2.invoke(fci.i2, obj, args);
            } catch (InvocationTargetException e) {
                throw e.getTargetException();
            }
        }
    }
    

    从上面的代码分析中,我们可以得知 MethodProxy 类有两个主要的方法。

    • invoke:调用代理类中被调用的方法,即 Service$$EnhancerByCGLIB$$bdabbd96 中的 publicMethod 方法。根据 Java 多态特性可知:

      • 如果果传入代理对象,即调用代理类的增强方法,
      • 如果传入目标对象,调用未增强的目标类的目标方法。
    • invokeSuper: 直接在代理类中调用父类的方法,即 Service$$EnhancerByCGLIB$$bdabbd96 中的 CGLIB$publicMethod$0 方法。

    FastClass 机制

    还记得之前两个 FastClass 文件吗?

    • Service$$FastClassByCGLIB$$fdf36b96 是被代理类的 FastClass。
    • Service$$EnhancerByCGLIB$$bdabbd96$$FastClassByCGLIB$$a112cdb2 是代理类的 FastClass。

    CGLIB 动态代理执行代理方法效率之所以比JDK 动态代理高,是因为 CGLIB 采用了 FastClass 机制。

    下面我们先来看一下被代理类的 FastClass Service$$FastClassByCGLIB$$fdf36b96

    package com.wilimm.proxy.cglib;
    
    import java.lang.reflect.InvocationTargetException;
    import net.sf.cglib.core.Signature;
    import net.sf.cglib.reflect.FastClass;
    
    public class Service$$FastClassByCGLIB$$fdf36b96 extends FastClass {
        public Service$$FastClassByCGLIB$$fdf36b96(Class var1) {
            super(var1);
        }
        
        // 根据方法签名,获取到 FastClass 中的方法 index 值
        public int getIndex(Signature var1) {
            String var10000 = var1.toString();
            switch(var10000.hashCode()) {
            case -560613858:
                if (var10000.equals("finalMethod()V")) {
                    return 1;
                }
                break;
            case -433379701:
                if (var10000.equals("publicMethod()V")) {
                    return 0;
                }
                break;
            }
    
            return -1;
        }
        
         /**
         * 调用 FastClass 中的方法
         * @param var1 方法 index 值
         * @param var2 方法所在的类的对象
         * @param var3 参数列表
         */
        public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
            // 强制转换 Object 为 Service 类型,从而可以直接调用 Service 中定义的方法
            Service var10000 = (Service)var2;
            int var10001 = var1;
    
            try {
                switch(var10001) {
                case 0:
                    var10000.publicMethod();
                    return null;
                case 1:
                    var10000.finalMethod();
                    return null;
                case 2:
                    return new Boolean(var10000.equals(var3[0]));
                case 3:
                    return var10000.toString();
                case 4:
                    return new Integer(var10000.hashCode());
                }
            } catch (Throwable var4) {
                throw new InvocationTargetException(var4);
            }
    
            throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
    }
    

    然后我们再来看一下代理类的 FastClass Service$$EnhancerByCGLIB$$bdabbd96$$FastClassByCGLIB$$a112cdb2

    package com.wilimm.proxy.cglib;
    
    import com.wilimm.proxy.cglib.Service..EnhancerByCGLIB..bdabbd96;
    import java.lang.reflect.InvocationTargetException;
    import net.sf.cglib.core.Signature;
    import net.sf.cglib.proxy.Callback;
    import net.sf.cglib.reflect.FastClass;
    
    public class Service$$EnhancerByCGLIB$$bdabbd96$$FastClassByCGLIB$$a112cdb2 extends FastClass {
        public Service$$EnhancerByCGLIB$$bdabbd96$$FastClassByCGLIB$$a112cdb2(Class var1) {
            super(var1);
        }
        
        // 根据方法签名,获取到 FastClass 中的方法 index 值  
        public int getIndex(Signature var1) {
            String var10000 = var1.toString();
            switch(var10000.hashCode()) {
            case -560613858:
                if (var10000.equals("finalMethod()V")) {
                    return 21;
                }
                break;
            case -433379701:
                if (var10000.equals("publicMethod()V")) {
                    return 9;
                }
                break;
            case 920107676:
                if (var10000.equals("CGLIB$publicMethod$0()V")) {
                    return 16;
                }
                break;
            return -1;
        }
    
       /**
         * 调用 FastClass 中的方法
         * @param var1 方法 index 值
         * @param var2 方法所在的类的对象
         * @param var3 参数列表
         */
        public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
            // 强制转换 Object 为 bdabbd96 代理类类型,从而可以直接调用 代理类中定义的方法
            bdabbd96 var10000 = (bdabbd96)var2;
            int var10001 = var1;
    
            try {
                switch(var10001) {
                case 0:
                    return new Boolean(var10000.equals(var3[0]));
                case 1:
                    return var10000.toString();
                case 2:
                    return new Integer(var10000.hashCode());
                case 3:
                    return var10000.clone();
                case 4:
                    return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
                case 5:
                    return var10000.newInstance((Callback[])var3[0]);
                case 6:
                    return var10000.newInstance((Callback)var3[0]);
                case 7:
                    var10000.setCallbacks((Callback[])var3[0]);
                    return null;
                case 8:
                    var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
                    return null;
                case 9:
                    var10000.publicMethod();
                    return null;
                case 10:
                    bdabbd96.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
                    return null;
                case 11:
                    bdabbd96.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
                    return null;
                case 12:
                    return var10000.getCallbacks();
                case 13:
                    return var10000.getCallback(((Number)var3[0]).intValue());
                case 14:
                    return bdabbd96.CGLIB$findMethodProxy((Signature)var3[0]);
                case 15:
                    bdabbd96.CGLIB$STATICHOOK1();
                    return null;
                case 16:
                    var10000.CGLIB$publicMethod$0();
                    return null;
                case 17:
                    return var10000.CGLIB$clone$4();
                case 18:
                    return new Integer(var10000.CGLIB$hashCode$3());
                case 19:
                    return var10000.CGLIB$toString$2();
                case 20:
                    return new Boolean(var10000.CGLIB$equals$1(var3[0]));
                case 21:
                    var10000.finalMethod();
                    return null;
                }
            } catch (Throwable var4) {
                throw new InvocationTargetException(var4);
            }
    
            throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
    }
    

    FastClass 的原理简单来说就是:为不需要反射 invoke 调用的原类型生成一个 FastClass 类,然后给原类型的方法分配一个 index,在生成的 FastClass 中的 invoke 方法中,先直接把 Object 强制转换为原类型,然后根据这个 index,就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比 JDK 动态代理通过反射调用高。

    至此,我们基本上了解了 CGLIB 动态代理的实现原理。

    CGLIB 和 JDK 动态代理区别

    说完了 CGLIB 动态代理和 JDK 动态代理之后,我们总结一下两者的区别:

    • JDK 动态代理基于接口,CGLIB 动态代理基于类。因为 JDK 动态代理生成的代理类需要继承 java.lang.reflect.Proxy,而 Java 只支持单继承,所以只能基于接口。

    • JDK 动态代理和 CGLIB 动态代理都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLIB 使用 ASM 框架写 Class 字节码。

    • JDK 通过反射机制调用方法,CGLIB 通过 FastClass 机制直接调用方法,所以 CGLIB 执行的效率更高。

    (正文完)

    本文所用代码地址

    扩展阅读

    喜欢本文的朋友们,欢迎关注微信公众号【程序员小课堂】,阅读更多精彩内容!


    程序员小课堂.jpg

    相关文章

      网友评论

        本文标题:Spring AOP (三) CGLIB 动态代理

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