在Spring Aop的实现中,动态代理有2种实现:第一种是JDK自带的,在读源码——JDK动态代理写过了;第二种就是本文要写的cglib动态代理的实现了。
在Spring Aop的目录org.springframework.aop.framework下DefaultAopProxyFactory类完成了主要的代理生成过程,可以看得出来,Spring还是优先使用Cglib做动态代理的。
cglib即Code Generation Library,做动态代理其实只是cglib一方面的应用。其实cglib是一个功能强大的高性能高质量代码生成库,它底层依赖于小而快的字节码处理框架ASM,来扩展JAVA类并在运行时实现接口。
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());
}
}
"C:\Program Files\Java\jdk1.8.0_221\bin\java.exe"...
before
hello world
after
class CglibClass$$EnhancerByCGLIB$$b18fede1
Process finished with exit code 0
Enhancer: 字节码增强器,类似于JDK动态代理的Proxy。不过Enhancer不但可以代理类,也可以代理接口。但是它不能拦截final的方法,被final修饰的方法不能被重写,你懂得的。
Callback: 回调,它是个空接口,类似于JDK动态代理的InvocationHandler,在调用代理类的时候触发。也可以设置多个。最常用的是代码中写的那个,当然它还有一些其他的子接口:
调用栈:
(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
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。
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);
}
//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>>。
// /生成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倍左右。
其实jdk动态代理慢主要还是慢在了对于反射的应用上,而cglib相对于jdk实现的动态代理在反射的应用上则是能省则省,以上介绍过的EnhancerFactoryData实现就是一个例子。
通过将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
b18fede1
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的正向结合,替换了性能较差的反射。
以上。。。