读源码——cglib动态代理

时间:2022-07-23
本文章向大家介绍读源码——cglib动态代理,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

背景

在Spring Aop的实现中,动态代理有2种实现:第一种是JDK自带的,在读源码——JDK动态代理写过了;第二种就是本文要写的cglib动态代理的实现了。

在Spring Aop的目录org.springframework.aop.framework下DefaultAopProxyFactory类完成了主要的代理生成过程,可以看得出来,Spring还是优先使用Cglib做动态代理的。

cglib应用

cglib即Code Generation Library,做动态代理其实只是cglib一方面的应用。其实cglib是一个功能强大的高性能高质量代码生成库,它底层依赖于小而快的字节码处理框架ASM,来扩展JAVA类并在运行时实现接口。

1. 简单实现

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibClass {

    public void test() {
        System.out.println("hello world");
    }
    public void code(){
        System.out.println("codeing");
    }

    public static void main(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "target/cglib");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CglibClass.class);
        enhancer.setCallback(new MethodInterceptor() {
            /**
             *
             * @param obj 代理对象
             * @param method 被代理的方法
             * @param args 参数
             * @param methodProxy 代理方法
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("before");
                Object result = methodProxy.invokeSuper(obj,args);
                System.out.println("after");
                return result;
            }
        });
        CglibClass sampleClass = (CglibClass) enhancer.create();
        sampleClass.test();
        System.out.println(sampleClass.getClass());
    }
}

2. 结果展示

"C:Program FilesJavajdk1.8.0_221binjava.exe"...
before
hello world
after
class CglibClass$$EnhancerByCGLIB$$b18fede1

Process finished with exit code 0

Enhancer: 字节码增强器,类似于JDK动态代理的Proxy。不过Enhancer不但可以代理类,也可以代理接口。但是它不能拦截final的方法,被final修饰的方法不能被重写,你懂得的。

Callback: 回调,它是个空接口,类似于JDK动态代理的InvocationHandler,在调用代理类的时候触发。也可以设置多个。最常用的是代码中写的那个,当然它还有一些其他的子接口:

  • NoOp :如其名,什么也不做;
  • FixedValue:有一个loadObject方法,可返回固定值;
  • InvocationHandler:类似于jdk实现的InvocationHandler;
  • MethodInterceptor:以上代码所实现的,可以用来实现环绕通知;
  • Dispatcher:也有一个loadObject方法,特点是每次都会重新调用这个方法,区别于LazyLoader;
  • LazyLoader:懒加载,它也只有一个loadObject方法,特点是多次调用,只调用loadObject一次;

3. 源码解读

调用栈:

(1). enhancer.create() //目的是获取代理类对象
    => (2) Enhancer.createHelper() //完成一个多值key(也就是subKey)的创建
     =>(3)AbstractClassGenerator#create(key)//一维缓存
      =>(4)AbstractClassGenerator#ClassLoaderData#get(AbstractClassGenerator gen, boolean useCache)//是否用缓存
       =>(5)LoadingCache#get(K key)//二维缓存
        =>(6) Enhancer#generate(ClassLoaderData data)//校验,设置NamePrefix
         =>(7) AbstractClassGenerator#generate(ClassLoaderData data)//生成class

(3)AbstractClassGenerator#create

protected Object create(Object key) {
    try {
        ClassLoader loader = getClassLoader();.//获取classLoader,它是第一维度缓存的key
        Map<ClassLoader, ClassLoaderData> cache = CACHE;
        ClassLoaderData data = cache.get(loader);
        //下边是一个双重检测加锁单例模式
        if (data == null) {
            synchronized (AbstractClassGenerator.class) {
                cache = CACHE;
                data = cache.get(loader);
                if (data == null) {
                    Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
                    data = new ClassLoaderData(loader);
                    newCache.put(loader, data);
                    CACHE = newCache;
                }
            }
        }
        this.key = key;
        Object obj = data.get(this, getUseCache());//第二维度缓存值获取Class,有走缓存和不走缓存两条路
        if (obj instanceof Class) {//如果获取的是类(不走缓存获得的)
            return firstInstance((Class) obj);//通过Class获取对象
        }
        return nextInstance(obj);//如果是对象,走缓存获得的EnhancerFactoryData 对象;默认userCache=true
    } catch (RuntimeException e) {
        throw e;
    } catch (Error e) {
        throw e;
    } catch (Exception e) {
        throw new CodeGenerationException(e);
    }
}

其实cglib的源码实现和Jdk的实现上有很多相似之处,尤其是对于弱缓存的应用上,cglib也是弱引用实现的二维映射表。不过略有不同的是cglib使用的是jdk自己使用的WeakHashMap。

(4)AbstractClassGenerator#ClassLoaderData#get

private static final Function<AbstractClassGenerator, Object> GET_KEY = new Function<AbstractClassGenerator, Object>() {
    public Object apply(AbstractClassGenerator gen) {
        return gen.key;
    }
};
public ClassLoaderData(ClassLoader classLoader) {
    if (classLoader == null) {
        throw new IllegalArgumentException("classLoader == null is not yet supported");
    }
    this.classLoader = new WeakReference<ClassLoader>(classLoader);
    Function<AbstractClassGenerator, Object> load =
            new Function<AbstractClassGenerator, Object>() {
                public Object apply(AbstractClassGenerator gen) {
                    Class klass = gen.generate(ClassLoaderData.this);//生成代理类class
                    return gen.wrapCachedClass(klass);
                }
            };
    generatedClasses = new LoadingCache<AbstractClassGenerator, Object, Object>(GET_KEY, load);
}
public Object get(AbstractClassGenerator gen, boolean useCache) {
    if (!useCache) {//useCache默认为true
      return gen.generate(ClassLoaderData.this);
    } else {
      Object cachedValue = generatedClasses.get(gen);
      return gen.unwrapCachedValue(cachedValue);
    }
}

以上贴了两段代码,分别是ClassLoaderData的构造函数以及get方法,可以很清楚的看到二维缓存值的获取过程。generatedClasses是一个LoadingCache对象,LoadingCache就像是一个抽象工厂缓存类,内有三个重要的属性值:

//传入GET_KEY,包装缓存key值的实现
keyMapper = keyMapper;
//传入load,包装缓存value的实现
this.loader = loader;
////新建一个ConcurrentHashMap,是缓存载体
this.map = new ConcurrentHashMap<KK, Object>();

再说一下这个loader的实现,共两行代码,第一行完成class的创建,调用的是Enhancer#generate(ClassLoaderData data)方法,第6、7步会说。gen.unwrapCachedValue(cachedValue)代码如下。可以清楚看到EnhancerFactoryData缓存了一些通过反射获取到的属性和方法,相比来说速度肯定要快一些的。

@Override
protected Object wrapCachedClass(Class klass) {
    Class[] argumentTypes = this.argumentTypes;
    if (argumentTypes == null) {
        argumentTypes = Constants.EMPTY_CLASS_ARRAY;
    }
    EnhancerFactoryData factoryData = new EnhancerFactoryData(klass, argumentTypes, classOnly);
    Field factoryDataField = null;
    try {
        // The subsequent dance is performed just once for each class,
        // so it does not matter much how fast it goes
        factoryDataField = klass.getField(FACTORY_DATA_FIELD);
        factoryDataField.set(null, factoryData);
        Field callbackFilterField = klass.getDeclaredField(CALLBACK_FILTER_FIELD);
        callbackFilterField.setAccessible(true);
        callbackFilterField.set(null, this.filter);
    } catch (NoSuchFieldException e) {
        throw new CodeGenerationException(e);
    } catch (IllegalAccessException e) {
        throw new CodeGenerationException(e);
    }
    return new WeakReference<EnhancerFactoryData>(factoryData);
}

(5)LoadingCache#get

//LoadingCache.get(key)
public V get(K key) {
    final KK cacheKey = keyMapper.apply(key);//获取多值key
    Object v = map.get(cacheKey);
    if (v != null && !(v instanceof FutureTask)) {
        return (V) v;
    }

    return createEntry(key, cacheKey, v);//创建缓存值
}
 protected V createEntry(final K key, KK cacheKey, Object v) {
    FutureTask<V> task;
    boolean creator = false;
    if (v != null) {
        //判断是否已经被其他线程完成创建
        task = (FutureTask<V>) v;
    } else {
        task = new FutureTask<V>(new Callable<V>() {
            public V call() throws Exception {
                return loader.apply(key);//执行loader
            }
        });
        Object prevTask = map.putIfAbsent(cacheKey, task);
        if (prevTask == null) {
            // creator does the load
            creator = true;
            task.run();
        } else if (prevTask instanceof FutureTask) {
            task = (FutureTask<V>) prevTask;
        } else {
            return (V) prevTask;
        }
    }

    V result;
    try {
        result = task.get();//执行FutureTask
    } catch (InterruptedException e) {
        throw new IllegalStateException("Interrupted while loading cache item", e);
    } catch (ExecutionException e) {
        Throwable cause = e.getCause();
        if (cause instanceof RuntimeException) {
            throw ((RuntimeException) cause);
        }
        throw new IllegalStateException("Unable to load cache item", cause);
    }
    if (creator) {
        map.put(cacheKey, result);
    }
    return result;
}

走到这里,可以看清楚,其实二维缓存是WeakHashMap<ClassLoader,CurrentHashMap<multiKeys,Class/FutureTask>>。

(7)AbstractClassGenerator#generate(ClassLoaderData data)

// /生成class
protected Class generate(ClassLoaderData data) {
    Class gen;
    Object save = CURRENT.get();
    CURRENT.set(this);
    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) {
          String name = generateClassName(data.getUniqueNamePredicate());  //组装类名,一般是:类名$$来源(生成该类的简单类名)ByCGLIB$$mul_key值hash[_index]
          data.reserveName(name);
          this.setClassName(name);
        }
        if (attemptLoad) {
            try {
                gen = classLoader.loadClass(getClassName());
                return gen;
            } catch (ClassNotFoundException e) {
                // ignore
            }
        }
        byte[] b = strategy.generate(this);//默认策略,生成类;代码行1
        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);
    }
}

整个效果看下来,和JDK动态代理差不太多。那么为什么Spring要优先使用cglib做动态代理呢?因为它相对较快,研究表明cglib动态代理比jdk动态代理速度快10倍左右。

cglib为什么快?

1. EnhancerFactoryData实现为cglib提速

其实jdk动态代理慢主要还是慢在了对于反射的应用上,而cglib相对于jdk实现的动态代理在反射的应用上则是能省则省,以上介绍过的EnhancerFactoryData实现就是一个例子。

2. FastClass为cglib提速

查看生成字节码

通过将DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,设置为"target/cglib"(也可以是其他的,代表打印字节码文件的路径)可以打印它的字节码。原理是:代码行1调用了DebuggingClassWriter#toByteArray方法,其中有个全局属性debugLocation,就是通过DEBUG_LOCATION_PROPERTY设置的。如图,如果使用和我一样的实现方式的话,在target/cglib目录下关于CglibClass的类会有三个。

CglibClass$$EnhancerByCGLIB$$b18fede1.class

 public final void test() {
    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$test$0$Method, CGLIB$emptyArgs, CGLIB$test$0$Proxy);//调用intercept方法,最后一个参数是MethodProxy,它的对象在static代码块创建
    } else {
        super.test();//如果没有回调方法,就调用父类的
    }
}

methodProxy.invokeSuper(obj,args)发生了什么

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        init();//完成了另外两个FastClass类的创建,f1是CglibClass的fastClass,f2是CglibClass的代理类的fastClass。
        FastClassInfo fci = fastClassInfo;
        return fci.f2.invoke(fci.i2, obj, args);//这句并不是反射,而是直接调用了生成class中的方法。
    } catch (InvocationTargetException e) {
        throw e.getTargetException();
    }
}

这是它的test()方法,也就是我们的代理方法。

CglibClass

EnhancerByCGLIB

b18fede1

FastClassByCGLIB

78328f37.class

public int getIndex(Signature var1) {
      String var10000 = var1.toString();
      switch(var10000.hashCode()) {
      case -1659809612:
          if (var10000.equals("CGLIB$test$0()V")) {
              return 16;
          }
          break;
      case -1422510685:
          if (var10000.equals("test()V")) {
              return 7;
          }
          break;
      }
      ...
      return -1;
  }

这是FastClass一个最为主要的实现(代码已经精简),通过对每个方法编号,当调用的时候,将编号再转化成具体实现,完成了将对象和method的正向结合,替换了性能较差的反射。

以上。。。