Spring AOP (三) CGLIB 动态代理

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

首先,导入 CGLIB 的 Maven 依赖。



  cglib
  cglib
  3.2.11

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



  org.springframework
  spring-aop
  5.1.6.RELEASE

这里选择导入 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 
     * @return
     */
    public  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();
    }
}

测试结果如下所示:

Spring AOP (三) CGLIB 动态代理_第1张图片
测试结果.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 CACHE = new WeakHashMap();

protected Object create(Object key) {
    Map 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 文件,如下图所示。

Spring AOP (三) CGLIB 动态代理_第2张图片
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 执行的效率更高。

(正文完)

本文所用代码地址

扩展阅读

  • Spring AOP (一) 简介
  • Spring AOP (二) JDK 动态代理

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


Spring AOP (三) CGLIB 动态代理_第3张图片
程序员小课堂.jpg

你可能感兴趣的:(Spring AOP (三) CGLIB 动态代理)