cglib源码分析(一): 缓存和KEY

时间:2023-03-08 21:20:21

cglib是一个java 字节码的生成工具,它是对asm的进一步封装,提供了一系列class generator。研究cglib主要是因为它也提供了动态代理功能,这点和jdk的动态代理类似。

一、 Cache的创建

与jdk动态代理一样,cglib也提供了缓存来提高系统的性能,对于已经生成的类,直接使用而不必重复生成。这里不得不提到一个比较重要的抽象类AbstractClassGenerator,它采用了模版方法的设计模式,protected Object create(Object key) 就是模版方法,它定义了类生成的过程。AbstractClassGenerator只有一个构造函数protected AbstractClassGenerator(Source source),入参是一个Source类型的对象,Source是AbstractClassGenerator里面的一个静态内部类,Source有两个字段 name用来记录class generator,cache 就是缓存,它和jdk动态代理一样都是用了WeakHashMap,并且类型也是<ClassLoader,<Object,Class>>:

    protected static class Source {
String name; //class generator的name,eg:如果使用Enhancer来生成增强类,name的值就为 net.sf.cglib.proxy. Enhancer
Map cache = new WeakHashMap();
public Source(String name) {
this.name = name;
}
}

每个class generator都必须继承AbstractClassGenerator并且实现 public void generateClass(ClassVisitor v) 方法用来生成所需要的类。每个class generator都有独立的缓存,比如 Enhancer 类中 private static final Source SOURCE = new Source(Enhancer.class.getName()); 在BeanGenerator 中 private static final Source SOURCE = new Source(BeanGenerator.class.getName()); 

二、 Cache的使用

缓存的使用主要封装在AbstractClassGenerator的模版方法create中,下面是create方法的源码:

synchronized (source) {
ClassLoader loader = getClassLoader();
Map cache2 = null;
cache2 = (Map)source.cache.get(loader);
if (cache2 == null) {
cache2 = new HashMap();
cache2.put(NAME_KEY, new HashSet());
source.cache.put(loader, cache2);
} else if (useCache) {
Reference ref = (Reference)cache2.get(key);
gen = (Class) (( ref == null ) ? null : ref.get());
}
…… 忽略若干代码
if (useCache) {
cache2.put(key, new WeakReference(gen));
}
}

生成类的缓存是按照ClassLoader来划分的,这是因为类的区分不仅根据类名还根据装载类的ClassLoader,也就是说同一个类被不同的ClassLoader加载,那么它们也是不同的,关于这部分内容可参考 http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.3 。每个ClassLoader的缓存中都会有一个NAME_KEY 这个主要是用来对生成的class name进行去重,NAME_KEY 会在生成类命名策略里有进一步的说明。此处还使用useCache 来标记是否使用缓存,这给了用户比较灵活的选择。

三、Key的例子

每个生成类在缓存中都会有一个key与之相对应。对于那些只与单个类相关的生成类,可以采用类名作为key。在动态代理中生成类不仅与目标类相关,还与使用的拦截类(MethodInterceptor),过滤类(CallbackFilter)相关,这样的话就要使用multi-vaules key来标识这个生成类,在cglib中multi-vaules 也是动态生成的,KeyFactory  就是生成multi-vaules的工厂类,它是一个抽象类,也就是说它不能被实例化,但是它提供了一系列的静态工厂方法来生成multi-vaules的工厂类,这里很拗口,下面是cglib源码包中的一个例子:

package samples;
import net.sf.cglib.core.KeyFactory;
public class KeySample {
private interface MyFactory {
public Object newInstance(int a, char[] b, String d);
}
public static void main(String[] args) {
MyFactory f = (MyFactory)KeyFactory.create(MyFactory.class);
Object key1 = f.newInstance(20, new char[]{ 'a', 'b' }, "hello");
Object key2 = f.newInstance(20, new char[]{ 'a', 'b' }, "hello");
Object key3 = f.newInstance(20, new char[]{ 'a', '_' }, "hello");
System.out.println(key1.equals(key2));
System.out.println(key1.toString());
System.out.println(key2.equals(key3));
}
}

运行结果是:

true
20, {a, b}, hello
false

为了生成multi-vaules 的工厂类,我们必须提供一个接口来描述multi-vaules的结构(上例中该接口为MyFactory),该接口有且只有一个方法newInstance,该方法的返回值必须为Object,该方法的入参可以是任意的对象,元数据类型 或者是任意维的数组 但是入参不能为空,如果为空就和默认的构造函数相冲突。 在分析KeyFactory之前,我们将上例生成的multi-vaules工厂类进行一下反编译(jd-gui 由于版本的问题无法反编译,因而此处使用javap):

public class samples.KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e extends net.sf.cglib.core.KeyFactory implements samples.KeySample$MyFactory{
public samples.KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e();
Code:
0: aload_0
1: invokespecial #11; //Method net/sf/cglib/core/KeyFactory."<init>":()V
4: return public java.lang.Object newInstance(int, char[], java.lang.String);
Code:
0: new #2; //class samples/KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e
3: dup
4: iload_1
5: aload_2
6: aload_3
7: invokespecial #16; //Method "<init>":(I[CLjava/lang/String;)V
10: areturn public samples.KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e(int, char[], java.lang.String);
Code:
0: aload_0
1: invokespecial #11; //Method net/sf/cglib/core/KeyFactory."<init>":()V
4: aload_0
5: dup
6: iload_1
7: putfield #20; //Field FIELD_0:I
10: dup
11: aload_2
12: putfield #24; //Field FIELD_1:[C
15: dup
16: aload_3
17: putfield #28; //Field FIELD_2:Ljava/lang/String;
20: return public int hashCode();
Code:
0: sipush 179
3: aload_0
4: getfield #20; //Field FIELD_0:I
7: swap
8: ldc #31; //int 467063
10: imul
11: swap
12: iadd
13: aload_0
14: getfield #24; //Field FIELD_1:[C
17: dup
18: ifnull 48
21: astore_1
22: iconst_0
23: istore_2
24: goto 39
27: aload_1
28: iload_2
29: caload
30: swap
31: ldc #31; //int 467063
33: imul
34: swap
35: iadd
36: iinc 2, 1
39: iload_2
40: aload_1
41: arraylength
42: if_icmplt 27
45: goto 49
48: pop
49: aload_0
50: getfield #28; //Field FIELD_2:Ljava/lang/String;
53: swap
54: ldc #31; //int 467063
56: imul
57: swap
58: dup
59: ifnull 68
62: invokevirtual #35; //Method java/lang/Object.hashCode:()I
65: goto 70
68: pop
69: iconst_0
70: iadd
71: ireturn public boolean equals(java.lang.Object);
Code:
0: aload_1
1: instanceof #2; //class samples/KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e
4: ifeq 133
7: aload_0
8: getfield #20; //Field FIELD_0:I
11: aload_1
12: checkcast #2; //class samples/KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e
15: getfield #20; //Field FIELD_0:I
18: if_icmpne 133
21: aload_0
22: getfield #24; //Field FIELD_1:[C
25: aload_1
26: checkcast #2; //class samples/KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e
29: getfield #24; //Field FIELD_1:[C
32: dup2
33: ifnonnull 43
36: ifnonnull 49
39: pop2
40: goto 93
43: ifnull 49
46: goto 53
49: pop2
50: goto 133
53: dup2
54: arraylength
55: swap
56: arraylength
57: if_icmpeq 64
60: pop2
61: goto 133
64: astore_2
65: astore_3
66: iconst_0
67: istore 4
69: goto 86
72: aload_2
73: iload 4
75: caload
76: aload_3
77: iload 4
79: caload
80: if_icmpne 133
83: iinc 4, 1
86: iload 4
88: aload_2
89: arraylength
90: if_icmplt 72
93: aload_0
94: getfield #28; //Field FIELD_2:Ljava/lang/String;
97: aload_1
98: checkcast #2; //class samples/KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e
101: getfield #28; //Field FIELD_2:Ljava/lang/String;
104: dup2
105: ifnonnull 115
108: ifnonnull 121
111: pop2
112: goto 131
115: ifnull 121
118: goto 125
121: pop2
122: goto 133
125: invokevirtual #39; //Method java/lang/Object.equals:(Ljava/lang/Object;)Z
128: ifeq 133
131: iconst_1
132: ireturn
133: iconst_0
134: ireturn public java.lang.String toString();
Code:
0: new #43; //class java/lang/StringBuffer
3: dup
4: invokespecial #44; //Method java/lang/StringBuffer."<init>":()V
7: aload_0
8: getfield #20; //Field FIELD_0:I
11: invokevirtual #48; //Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;
14: goto 23
17: pop
18: ldc #50; //String null
20: invokevirtual #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
23: ldc #55; //String ,
25: invokevirtual #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
28: aload_0
29: getfield #24; //Field FIELD_1:[C
32: dup
33: ifnull 96
36: swap
37: ldc #57; //String {
39: invokevirtual #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
42: swap
43: astore_1
44: iconst_0
45: istore_2
46: goto 72
49: aload_1
50: iload_2
51: caload
52: invokevirtual #60; //Method java/lang/StringBuffer.append:(C)Ljava/lang/StringBuffer;
55: goto 64
58: pop
59: ldc #50; //String null
61: invokevirtual #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
64: ldc #55; //String ,
66: invokevirtual #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
69: iinc 2, 1
72: iload_2
73: aload_1
74: arraylength
75: if_icmplt 49
78: dup
79: dup
80: invokevirtual #63; //Method java/lang/StringBuffer.length:()I
83: iconst_2
84: isub
85: invokevirtual #67; //Method java/lang/StringBuffer.setLength:(I)V
88: ldc #69; //String }
90: invokevirtual #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
93: goto 102
96: pop
97: ldc #50; //String null
99: invokevirtual #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
102: ldc #55; //String ,
104: invokevirtual #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
107: aload_0
108: getfield #28; //Field FIELD_2:Ljava/lang/String;
111: dup
112: ifnull 124
115: invokevirtual #71; //Method java/lang/Object.toString:()Ljava/lang/String;
118: invokevirtual #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
121: goto 130
124: pop
125: ldc #50; //String null
127: invokevirtual #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
130: invokevirtual #72; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
133: areturn }

从反编译的结果我们可以看出,生成的工厂类为samples.KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e,它继承了net.sf.cglib.core.KeyFactory 类,实现了samples.KeySample$MyFactory接口,同时也实现了工厂方法newInstance,所以上例中key1,key2,key3都是通过该工厂方法产生了key的对象。samples.KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e 中有两个构造函数,一个是默认的构造函数(由于工厂方法newInstance 为非静态方法,所以需要使用默认构造函数来生成第一个对象),另外一个构造函数的入参和 newInstance 入参一样,newInstance使用有参构造函数来生成multi-values key 对象。samples.KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e 为newInstance的每一个入参都生成了一个相同类型的field用来存储key的value。除此之外还提供了三个方法:hashCode, equals, toString.

hashCode 和 equals 在HashMap 的get方法中用于和存储的key进行比较,所以这两个方法是比较重要的。

四、KeyFactory 源码分析

KeyFactory的源码还是比较多的,接下来只对其中的关键代码进行分析:

        public void generateClass(ClassVisitor v) {
ClassEmitter ce = new ClassEmitter(v);
//对定义key工厂类结构的接口进行判断,判断该接口是否只有newInstance一个方法,newInstance的返回值是否为Object
Method newInstance = ReflectUtils.findNewInstance(keyInterface);
if (!newInstance.getReturnType().equals(Object.class)) {
throw new IllegalArgumentException("newInstance method must return Object");
} //获取newInstance的入参类型,此处使用ASM的Type来定义
Type[] parameterTypes = TypeUtils.getTypes(newInstance.getParameterTypes());
ce.begin_class(Constants.V1_2,
Constants.ACC_PUBLIC,
getClassName(),
KEY_FACTORY,
new Type[]{ Type.getType(keyInterface) },
Constants.SOURCE_FILE);
//生成默认构造函数
EmitUtils.null_constructor(ce); //生成newInstance 工厂方法
EmitUtils.factory_method(ce, ReflectUtils.getSignature(newInstance)); //生成有参构造方法
int seed = 0;
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
TypeUtils.parseConstructor(parameterTypes),
null);
e.load_this();
e.super_invoke_constructor();
e.load_this();
for (int i = 0; i < parameterTypes.length; i++) {
seed += parameterTypes[i].hashCode();
//为每一个入参生成一个相同类型的类字段
ce.declare_field(Constants.ACC_PRIVATE | Constants.ACC_FINAL,
getFieldName(i),
parameterTypes[i],
null);
e.dup();
e.load_arg(i);
e.putfield(getFieldName(i));
}
e.return_value();
e.end_method(); //生成hashCode
e = ce.begin_method(Constants.ACC_PUBLIC, HASH_CODE, null);
int hc = (constant != 0) ? constant : PRIMES[(int)(Math.abs(seed) % PRIMES.length)];
int hm = (multiplier != 0) ? multiplier : PRIMES[(int)(Math.abs(seed * 13) % PRIMES.length)];
e.push(hc);
for (int i = 0; i < parameterTypes.length; i++) {
e.load_this();
e.getfield(getFieldName(i));
EmitUtils.hash_code(e, parameterTypes[i], hm, customizer);
}
e.return_value();
e.end_method(); //生成equals函数,在equals函数中对每个入参都进行判断
e = ce.begin_method(Constants.ACC_PUBLIC, EQUALS, null);
Label fail = e.make_label();
e.load_arg(0);
e.instance_of_this();
e.if_jump(e.EQ, fail);
for (int i = 0; i < parameterTypes.length; i++) {
e.load_this();
e.getfield(getFieldName(i));
e.load_arg(0);
e.checkcast_this();
e.getfield(getFieldName(i));
EmitUtils.not_equals(e, parameterTypes[i], fail, customizer);
}
e.push(1);
e.return_value();
e.mark(fail);
e.push(0);
e.return_value();
e.end_method(); //生成toString方法
e = ce.begin_method(Constants.ACC_PUBLIC, TO_STRING, null);
e.new_instance(Constants.TYPE_STRING_BUFFER);
e.dup();
e.invoke_constructor(Constants.TYPE_STRING_BUFFER);
for (int i = 0; i < parameterTypes.length; i++) {
if (i > 0) {
e.push(", ");
e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
}
e.load_this();
e.getfield(getFieldName(i));
EmitUtils.append_string(e, parameterTypes[i], EmitUtils.DEFAULT_DELIMITERS, customizer);
}
e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING);
e.return_value();
e.end_method(); ce.end_class();
}