cglib源碼分析(一): 緩存和KEY


    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

}
View Code

 

從反編譯的結果我們可以看出,生成的工廠類為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();
        }
View Code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM