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(); }