首先得从类图开始,
总体流程说明:
通常的使用是直接通过Enhancer来创建代理对象,Enhancer继承于AbstractClassGenerator,AbstractClassGenerator实现了ClassGenerator,ClassGenerator接口的作用是能够通过一个ClassVisitor来生成byte[]。
在AbstractClassGenerator里面并没有直接实现了ClassGenerator#generateClass方法,而是给具体的实现类去实现,在不同的实现类里面能利用ClassVisitor来生成特定的类。比如说下图:
我们的重点在于AbstractClassGenerator。AbstractClassGenerator类里面增加了对类的缓存,提供了自定义的hook方法可以对代理类的名字修改,此外,还可以在应用之前做转化。
AbstractClassGenerator中主要的方法是create
方法。提供了一个key是ClassLoader, v是ClassLoaderData的缓存,默认是使用缓存的。可以通过set方法关闭掉,也可以通过修改环境变量cglib.useCache为false
来关闭掉。如果不走缓存,直接调用AbstractClassGenerator#generate(ClassLoaderData)方法
。
走缓存,要是缓存没有的话,最终还是会走到AbstractClassGenerator#generate(ClassLoaderData)方法
。只不过,多了一个缓存的操作。等会详细的说这个缓存。
在AbstractClassGenerator#generate(ClassLoaderData)
里面。首先要确定类的名字,并且类名字不能重复。还可以通过attemptLoad
来决定是否要先对这个类名先加载一下,如果加载成功了,就直接返回,attemptLoad
值是可以通过set方法来修改得。
这个时候类得名字已经确定了,就可以通过GeneratorStrategy
来生成一个byte[]了,这个时候已经在内存里面生成class文件了,通过ClassVisitor对生成得byte[]读取,直接读取class文件中得类名字,通过Class.forName获取class对象。
获取到Class对象之后,还可以通过AbstractClassGenerator#unwrapCachedValue
来做增强。
到这里,代理类已经搞好了,就剩最后一步,创建实例。在这里要做一个判断,因为它整个都是范型,这里它增加了判断,为什么要增加这个判断,因为在generate
方法里面很明确已经要返回class对象,将生成得class对象传递给unwrapCachedValue
,他得返回值是Object。这里面就保不齐出什么幺蛾子了。所以增加了判断。对于Class类型得直接调用firstInstance(Class )
,否则就是nextInstance(Object)
这俩方法都是留给子类来实现得。
带着上面的大体的流程概念,开始下面分析。
第一个问题是看看缓存,ClassLoaderData长什么样子
ClassLoaderData
这个类没有啥可说,提供了两个Function,GET_KEY(key的生成策略),load(value的加载方式),缓存真正的实现是在LoadingCache
里面,下面就看看他是怎么写的。
protected static class ClassLoaderData {
// 保存的是代理类的名字,
private final Set<String> reservedClassNames = new HashSet<String>();
// 缓存
private final LoadingCache<AbstractClassGenerator, Object, Object> generatedClasses;
private final WeakReference<ClassLoader> classLoader;
// 利用他来做代理类的唯一性
private final Predicate uniqueNamePredicate = new Predicate() {
public boolean evaluate(Object name) {
return reservedClassNames.contains(name);
}
};
// key的生成
private static final Function<AbstractClassGenerator, Object> GET_KEY = new Function<AbstractClassGenerator, Object>() {
public Object apply(AbstractClassGenerator gen) {
return gen.key;
}
};
// 构造方法,主要是创建了LoadingCache
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 = // 最后得get方法
new Function<AbstractClassGenerator, Object>() {
public Object apply(AbstractClassGenerator gen) {
Class klass = gen.generate(ClassLoaderData.this);
return gen.wrapCachedClass(klass);
}
};
generatedClasses = new LoadingCache<AbstractClassGenerator, Object, Object>(GET_KEY, load); // 创建loadCache
}
public ClassLoader getClassLoader() {
return classLoader.get();
}
public void reserveName(String name) {
reservedClassNames.add(name);
}
public Predicate getUniqueNamePredicate() {
return uniqueNamePredicate;
}
// 先从缓存中找,如果不需要走缓存,直接调用AbstractClassGenerator#generate方法,否则就要走缓存
public Object get(AbstractClassGenerator gen, boolean useCache) {
if (!useCache) {
return gen.generate(ClassLoaderData.this);
} else {
Object cachedValue = generatedClasses.get(gen);
return gen.unwrapCachedValue(cachedValue);
}
}
}
LoadingCache分析
在获取的时候,会通过传递进来的key(这里叫做主key),通过keyMapper来产生一个子key,子key用作缓存的key,通过主key去加载value。需要注意的是,value的加载是通过FutureTask来加载的,生成Class时候的线程安全
在ClassLoaderData使用它的时候,会将ClassLoaderData#GET_KEY和ClassLoaderData#load传递过来。所以LoadingCache本身就是一个外壳。内容是通过Function来加载的。这样我想到了缓存的实现。
缓存的实现,应该有下面的几个特点
- 缓存的吞吐率
- 缓存的命中率
- 其他
- 缓存大小
- 缓存的淘汰策略
- 缓存淘汰方式
- key和value的加载。
- 缓存的过期时间
其实我感觉,这个类在日常也是可以用的。
发现了一个问题,既然是hashMap,key是要用作hash计算的,问题来了,在Cglib中它是在那里引用的?
从上面可以知道,就是下图中的代码
是AbstractClassGenerator#key的key,那么它的hashCode是怎么保证的,它是在那里调用和赋值的。继续往下面看。
public class LoadingCache<K, KK, V> {
protected final ConcurrentMap<KK, Object> map; // 缓存的map
protected final Function<K, V> loader; // value的加载
protected final Function<K, KK> keyMapper;//keyMapper的生成
public static final Function IDENTITY = new Function() {
public Object apply(Object key) {
return key;
}
};
public LoadingCache(Function<K, KK> keyMapper, Function<K, V> loader) {
this.keyMapper = keyMapper;
this.loader = loader;
this.map = new ConcurrentHashMap<KK, Object>();
}
@SuppressWarnings("unchecked")
public static <K> Function<K, K> identity() {
return IDENTITY;
}
public V get(K key) {
final KK cacheKey = keyMapper.apply(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) {
// Another thread is already loading an instance
task = (FutureTask<V>) v;
} else {
task = new FutureTask<V>(new Callable<V>() {
public V call() throws Exception {
return loader.apply(key);
}
});
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();
} 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;
}
}
NamingPolicy(代理类名字的生成策略)
用来生成代理类的名字,方法有四个参数,prefix,source,key,names(在创建代理类名字的时候利用它来做唯一性,就是利用上面说的ClassLoaderData#uniqueNamePredicate
用来判断)。
看看source,如果source中有.
,就会取最后一个.
的值,如果没有.
就是全部,因为如果没有,lastIndexOf为-1,-1+1 = 0,就是全部。
剩下的三个的意思看它这里的代码逻辑就好了,我觉得重要的是看调用它的地方所赋予它的语义。还是老办法,点看调用看看。
都是AbstractClassGenerator中的属性,都提供了set方法来设置,namePerfix是String,没有啥可说的,但是source和key是什么?
source就是一个语义,就是用来生成名字的,key是一个Object。代表它是一个可以随便放的。那它的hashcode是怎么保证的,继续往下看。
为了解释清楚它的hashcode是怎么保证的。在此之前,我们回头看看AbstractClassGenerator#generate方法里面调用了GeneratorStrategy#generate方法。
public class DefaultNamingPolicy implements NamingPolicy {
public static final DefaultNamingPolicy INSTANCE = new DefaultNamingPolicy();
private final static boolean STRESS_HASH_CODE = Boolean.getBoolean("net.sf.cglib.test.stressHashCodes");
public String getClassName(String prefix, String source, Object key, Predicate names) {
if (prefix == null) {
prefix = "net.sf.cglib.empty.Object";
} else if (prefix.startsWith("java")) {
prefix = "$" + prefix;
}
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";
}
public int hashCode() {
return getTag().hashCode();
}
public boolean equals(Object o) {
return (o instanceof DefaultNamingPolicy) && ((DefaultNamingPolicy) o).getTag().equals(getTag());
}
}
在这个方法里面会调用key的hashcode值,所以,我们要看看key是在那里赋值的?还是老办法,点看看看调用
发现,在调用create方法的时候传递进来了key,随便点击一个方法,会发现下面的代码
KEY_FACTORY.newInstance(.......)的方法
直接说答案吧,是通过KeyFactory
来保证key的hashcode的值的。这也是动态生成的,在下面的章节会单独拿出来说。
到这里,理清了,Source和Key得作用。
Source是用作代理类名字生成得,他是作为中间值填充得。
Key是用作缓存得,同时还用于代理类名字得生成,他是作为后面得值填充得。(后面填充使用得是他得hashcode)。
GeneratorStrategy
DefaultGeneratorStrategy是GeneratorStrategy默认实现类,直接看它。
创建DebuggingClassWriter,DebuggingClassWriter继承于ClassVisitor,ClassVisitor是asm的类。
最终会调用到ClassGenerator#generateClass
,并将DebuggingClassWriter传递过来,留给子类通过DebuggingClassWriter来自定义代理类。
对了在toByteArray
方法有之前说的cglib.debugLocation
,会将生成的class文件保存在文件系统中。
当然,也可以重写transform()
方法来做ClassGenerator得转换或者生产得字节码得改变。这里是可以改变得,因为现在得byte[] 还没有加载。
public class DefaultGeneratorStrategy implements GeneratorStrategy {
public static final DefaultGeneratorStrategy INSTANCE = new DefaultGeneratorStrategy();
public byte[] generate(ClassGenerator cg) throws Exception {
DebuggingClassWriter cw = getClassVisitor();
transform(cg).generateClass(cw);
return transform(cw.toByteArray());
}
// DebuggingClassWriter 是继承于 ClassVisitor,lcnote 让我看看这个是干嘛得
// 回答 这个类是做 “主要负责 “拜访” 类成员信息。其中包括(标记在类上的注解,类的构造方法,类的字段,类的方法,静态代码块”
// [深入字节码 -- ASM 关键接口 ClassVisitor](https://blog.csdn.net/ywb201314/article/details/53117183)
protected DebuggingClassWriter getClassVisitor() throws Exception {
return new DebuggingClassWriter(ClassWriter.COMPUTE_FRAMES);
}
protected final ClassWriter getClassWriter() {
// Cause compile / runtime errors for people who implemented the old
// interface without using @Override
throw new UnsupportedOperationException("You are calling " +
"getClassWriter, which no longer exists in this cglib version.");
}
protected byte[] transform(byte[] b) throws Exception {
return b;
}
protected ClassGenerator transform(ClassGenerator cg) throws Exception {
return cg;
}
}
来到这里,就看看ClassGenerator#generateClass干了什么事情
ClassGenerator#generateClass
它有很多得实现类,每一个实现类里面都有自己自定义得操作,
这里就不挨个分析了,加上我对字节码得操作着实是不懂,就不详细得解释了。简单得说说Enhancer
得流程吧。
-
一开始得方法还是一波判断,比如说,常量修饰得类,是不能够做代理得。
-
确定被代理得类中得那些方法是不能代理得(对应代码是
Enhancer#getMethods
)-
拿到所有得方法。这里面就有的说了,他会拿到代理类的所有方法(包括父类,实现接口,接口的父接口),拿到方法之后,会有一个去重的操作
去重是通过HashSet来完成的,有一个小tips,主要是利用到了Hash,就要看它的hashCode方法,放在HashSet中的key的HashCode是怎么产生的。
利用
KeyFactory
来做的,对应的是(Enhancer#getMethods中调用的MethodWrapper#create
方法)。在它里面是通过KeyFactory
来保证Hashcode的。关于KeyFactory
,下面的章节会说,对了,突然想起来,还记得上面说的那个key吗?key的hash值也是通过**KeyFactory
**来做的。 -
增加过滤器来判断。(对应代码就是
Predicate
得实现类,和CollectionUtils#filter(Collection,Predicate)
方法),至于具体的方法能不能重写,(Public,Private,Protected)方法能不能重写,得看具体的实现类里面的判断逻辑了,但是Private方法不能重写
-
-
当然,构造方法也得来一下,除了Predicate之后(
Enhancer#filterConstructors
),多了一个transform
操作,具体的代码在Enhancer#generateClass里面调用的 CollectionUtils.transform(Collection,Transformer)方法
-
方法已经确定好了,下面开始得写字段了,这些字段并不是全部要用到的字段,只是一些固定的字段,具体变化的字段在方法写入的时候操作的。这些字段都是在代理类里面用的到的。
-
重点来了,下面要开始写方法了,因为Callback有很多的类型,
CallbackFilter
用来确定到底使用哪个Callback,这里肯定有这样的操作,对应的操作就是遍历,将method传递给filter,filter返回下标来确定到底要用哪个Callback。这一块对应的代码在(Enhancer#emitMethods
)此外,还提供了
CallbackGenerator
来做不同类型Callback的写入操作,将被代理的对象的方法按照类型分组,调用不同的CallbackGenerator来写入。
(callback类型和CallbackGenerator的映射关系)
不同的CallbackGenerator
有不同的处理逻辑,就拿Protected
修饰的方法来说,在LazyLoaderGenerator
,DispatcherGenerator
Protected修饰的方法是不能重写的,但是在别的里面就是可以的。
具体写方法的流程我就不分析了,因为我也不太清楚。
-
写完方法之后,还得写构造方法。
-
所有由Enhancer生成的代理类,是必须要实现Factory接口的,那么最后的操作就是实现Factory接口,并且生成代码。
到这里,代理类class文件已经生成了。剩下的几个步骤就是加载class文件,实例化了。下面说说**KeyFactory
**
KeyFactory分析
通过KeyFactory可以处理多个key,通过KeyFactory生成的对象,可以很方便的处理hashCode,和equals方法。并且生成的对象是遵从《 Effective Java 》中关于hashcode和equals的规定的。
此外,因为通过它来生成对象,这个对象得实现接口,否则KeyFactory它都不知道你有什么方法。所以,它要求接口必须的名字必须是newInstance
,返回值必须是Object
简单的说,这个类可方便的生成hashcode,和equals。参数不限制,只要方法名和返回值符合要求就行。
正因为它有这样的特性,所以,作为缓存的key,才能保证它的唯一性。也可以利用它来做Hashset的去重操作。
列子
-
接口
自定义的接口,需要传递三个参数作为hashcode和equals方法判断的因素。
package net.sf.cglib.samples.keyfactory; public interface TestKey { Object newInstance(String name,ClassLoader classLoader,String className); }
-
测试类
public class MainTest { public static void main(String[] args) { System.getProperties().setProperty("cglib.debugLocation", "C:\\Users\\Administrator\\Desktop\\temp"); TestKey keyFactory = (TestKey)KeyFactory.create(TestKey.class); Object a = keyFactory.newInstance("a", Thread.currentThread().getContextClassLoader(), TestKey.class.getName()); Object b = keyFactory.newInstance("a", Thread.currentThread().getContextClassLoader(), TestKey.class.getName()); System.out.println("a:hashCode" + a.hashCode()); System.out.println("b:hashCode" + b.hashCode()); System.out.println(a.equals(b)); System.out.println(a.getClass()); } }
-
生成的代理类
还是上面的老办法,将生成的class文件直接拖到idea里面去。
首先看属性,
newInstance
方法的入参都作为一个个属性,属性的名字是FIELD_开头,后面累加。从下面的代码可以看到,newInstance其实很简单,每次都是创建一个新的代理类。在它里面,每次都是赋值,new的操作。
重点是看的hashcode和equals方法。newInstance的属性,在这俩方法里面都用到了。
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package net.sf.cglib.samples.keyfactory; import net.sf.cglib.core.KeyFactory; public class TestKey$$KeyFactoryByCGLIB$$c8c30c06 extends KeyFactory implements TestKey { private final String FIELD_0; private final ClassLoader FIELD_1; private final String FIELD_2; public TestKey$$KeyFactoryByCGLIB$$c8c30c06() { } public Object newInstance(String var1, ClassLoader var2, String var3) { return new TestKey$$KeyFactoryByCGLIB$$c8c30c06(var1, var2, var3); } public TestKey$$KeyFactoryByCGLIB$$c8c30c06(String var1, ClassLoader var2, String var3) { this.FIELD_0 = var1; this.FIELD_1 = var2; this.FIELD_2 = var3; } public int hashCode() { int var10002 = 1209107 * 74391461; String var10001 = this.FIELD_0; var10002 = (var10002 + (var10001 != null ? var10001.hashCode() : 0)) * 74391461; ClassLoader var1 = this.FIELD_1; var10002 = (var10002 + (var1 != null ? var1.hashCode() : 0)) * 74391461; var10001 = this.FIELD_2; return var10002 + (var10001 != null ? var10001.hashCode() : 0); } public boolean equals(Object var1) { if (var1 instanceof TestKey$$KeyFactoryByCGLIB$$c8c30c06) { String var10000 = this.FIELD_0; String var10001 = ((TestKey$$KeyFactoryByCGLIB$$c8c30c06)var1).FIELD_0; if (var10001 == null) { if (var10000 != null) { return false; } } else if (var10000 == null || !var10000.equals(var10001)) { return false; } ClassLoader var2 = this.FIELD_1; ClassLoader var3 = ((TestKey$$KeyFactoryByCGLIB$$c8c30c06)var1).FIELD_1; if (var3 == null) { if (var2 != null) { return false; } } else if (var2 == null || !var2.equals(var3)) { return false; } var10000 = this.FIELD_2; var10001 = ((TestKey$$KeyFactoryByCGLIB$$c8c30c06)var1).FIELD_2; if (var10001 == null) { if (var10000 == null) { return true; } } else if (var10000 != null && var10000.equals(var10001)) { return true; } } return false; } public String toString() { StringBuffer var10000 = new StringBuffer(); String var10001 = this.FIELD_0; var10000 = (var10001 != null ? var10000.append(var10001.toString()) : var10000.append("null")).append(", "); ClassLoader var1 = this.FIELD_1; var10000 = (var1 != null ? var10000.append(var1.toString()) : var10000.append("null")).append(", "); var10001 = this.FIELD_2; return (var10001 != null ? var10000.append(var10001.toString()) : var10000.append("null")).toString(); } }
分析
直接从KeyFactory#create方法入手,在KeyFactory里面,有一个内部类Generator
,它继承了AbstractClassGenerator
接口,通过上面的分析,这里就直接看Generator#generateClass操作了。在这前面在看看几个常规的问题。
- Source是什么?
想想,Source的作用,所以,上面生成的代理类名字里面是有KeyFactory的。
- 既然继承了AbstractClassGenerator,那么在Generator里面,传递的缓存的key是啥?
传递的key是要实现的接口的名字。所以,同样的接口,再次通过KeyFactory来创建,就会命中缓存。
-
KeyFactoryCustomizer的作用是什么?
KeyFactoryCustomizer是KeyFactory#create方法的参数,它的作用是在构建equals,hashcode,toString方法时候的回调。通过它就可以自定义操作了。
开始看Generator#generateClass方法
这里我还是大体的说说流程,具体的就不说了,因为我也不太明白。具体的代码逻辑在KeyFactory#Generator#generateClass
里面。
-
首先第一件事情还是校验(方法名字的校验,方法返回值的校验)
-
这没有Enhancer那么复杂的操作。就那几个方法。先写一个无参构造。
-
在实现接口的方法,
-
声明字段(KeyFactory#getFieldName)
-
写方法(在这里会用到KeyFactoryCustomizer)
需要注意KeyFactoryCustomizer在之后用的时候有特殊的处理,会将所有的KeyFactoryCustomizer添加到
CustomizerRegistry
中,组成map,map的key是要处理的class,map的v是List<KeyFactoryCustomizer>
,具体的可以去看看CustomizerRegistry
创建和添加的相关代码.
到这里,代理对象创建的一个大体的流程就分析完了。结束。还有一个问题,Cglib中MethodInterceptor
拦截器来做方法调用的时候,会有什么操作,可记得在上一篇中,在生成的代理类中可是有MethodProxy
的操作的,剩下的几个Callback,没有什么特殊的地方。通过MethodProxy
来调用父类方法会发生什么?cglib是可以检测到被代理对象方法之间调用的吗?下一篇分析。
到此,结束了。
关于博客这件事,我是把它当做我的笔记,里面有很多的内容反映了我思考的过程,因为思维有限,不免有些内容有出入,如果有问题,欢迎指出。一同探讨。谢谢。