CGLIB中BeanCopier源碼實現

本文將簡要介紹CGLIB代碼包結構以及核心類的基本功能,然后通過介紹BeanCopier的使用例子,將其作為引子對相關源碼實現進行分析。

CGLIB代碼包結構

1.core

  • ClassGenerator(接口) & AbstractClassGenerator(實現類)
    • 作為cglib代碼中 最核心的調度者 ,封裝了類創建的主要流程,并支持一些緩存操作、命名策略(NamingPolicy)、代碼生成策略(GeneratorStrategy)等。
    • 其中,protected Object create(Object key) 作為模版方法,定義了類的生成過程。同時將變化點進行封裝,供繼承類自主實現。
  • GeneratorStrategy(接口) & DefaultGeneratorStrategy(默認實現類)
    • 控制ClassGenerator生成class的字節碼。
    • 為繼承類預留了兩個抽象方法,可以對生成的字節碼進行操作。
  • NamingPolicy(接口) & DefaultNamingPolicy(默認實現類)
    • 用于控制生成類的命名規則。
    • 一般的命名規則:
      • 被代理類名 + $$ + CGLIB核心處理類 + "ByCGLIB" + $$ + key的hashCode。
      • 示例:FastSource<span>$$</span>FastClassByCGLIB<span>$$</span>e1a36bab.class。
  • KeyFactory
    • 每個生成類都會在cglib的緩存中存在唯一的key與之對應,這個key就通過KeyFactory進行生成。
  • DebuggingClassWriter
    • 被DefaultGeneratorStrategy調用,將生成類轉為字節碼輸出。
    • 將生成的字節碼寫入到文件中(debugLocation)。
  • ClassEmitter & CodeEmitter
    • 封裝了ASM的實現,提供對類和方法的字節碼操作。
  • 工具類
    • EmitUtils:封裝了一些字節碼操作的基本函數。
    • ReflectUtils :封裝JDK中的反射操作。

2.beans

  • BeanCopier:用于兩個bean之間,同名屬性間的拷貝。
  • BulkBean:用于兩個bean之間,自定義get&set方法間的拷貝。
  • BeanMap:針對POJO Bean與Map對象間的拷貝。
  • BeanGenerator:根據Map<String,Class>properties的屬性定義,動態生成POJO Bean類。
  • ImmutableBean:同樣用于兩個bean間的屬性拷貝,但生成的bean不允許調用set方法,也就是說,生成的對象是不可變的。

3.reflect

  • FastClass & FastMethod
    • FastClass機制就是對一個類的方法建立索引,通過索引來直接調用相應的方法.

4.proxy

  • Enhancer: 用于生成動態代理類

此處略過了部分與本文無關的類,重點關注core包中的類即可~

更多細節可以參考這里

BeanCopier實現機制

1.BeanCopier的使用

顧名思義,BeanCopier是用于在兩個bean之間進行屬性拷貝的。BeanCopier支持兩種方式,一種是不使用Converter的方式,僅對兩個bean間屬性名和類型完全相同的變量進行拷貝。另一種則引入Converter,可以對某些特定屬性值進行特殊操作。

代碼如下所示。

不使用Converter的例子

    public void testSimple() {
        // 動態生成用于復制的類,false為不使用Converter類
        BeanCopier copier = BeanCopier.create(MA.class, MA.class, false);

        MA source = new MA();
        source.setIntP(42);
        MA target = new MA();

        // 執行source到target的屬性復制
        copier.copy(source, target, null);

        assertTrue(target.getIntP() == 42);
    }

使用Converter的例子

    public void testConvert() {
        // 動態生成用于復制的類,并使用Converter類
        BeanCopier copier = BeanCopier.create(MA.class, MA.class, true);

        MA source = new MA();
        source.setIntP(42);
        MA target = new MA();

        // 執行source到target的屬性復制
        copier.copy(source, target, new Converter() {

            /**
             * @param sourceValue source對象屬性值
             * @param targetClass target對象對應類
             * @param methodName targetClass里屬性對應set方法名,eg.setId
             * @return
             */
            public Object convert(Object sourceValue, Class targetClass, Object methodName) {
                if (targetClass.equals(Integer.TYPE)) {
                    return new Integer(((Number)sourceValue).intValue() + 1);
                }
                return sourceValue;
            }
        });

        assertTrue(target.getIntP() == 43);
    }

很簡單吧~

核心代碼就只有兩行:

copier = BeanCopier.create // 生成用于兩個bean間進行復制的類

copier.copy(source, target, converter) // 執行復制

2.性能分析

場景 耗時(1000000次調用) 原理
直接使用get&set方法 22ms 直接調用
使用BeanCopiers(不使用Converter) 22ms 修改字節碼
使用BeanCopiers(使用Converter) 249ms 修改字節碼
使用BeanUtils 12983ms 反射
使用PropertyUtils(不使用Converter) 3922ms 反射



從上面數據可以看出,不使用Converter時,BeanCopiers與直接調用get&set方法性能相當。

3.一次調用流程

接下來,我們來看看在簡單的調用背后,cglib替我們做了哪些事。

(1)CGLIB做了什么

CGLIB的核心在于通過操作字節碼生成類,來實現原本需要通過反射或者一堆代碼才能實現的邏輯。在我們剛剛的例子里(注意, 是不帶Converter的例子 ),CGLIB在背后悄悄替我們生成了兩個類,我們先來稍微窺探一下這兩個生成類,然后接下來的時間我們都將用來分析,cglib是如何生成這兩個類的。

  • 第一個類

public class MA$$BeanCopierByCGLIB$$d9c04262 extends BeanCopier {
    public MA$$BeanCopierByCGLIB$$d9c04262() {
    }

    public void copy(Object var1, Object var2, Converter var3) {
        MA var10000 = (MA)var2;
        MA var10001 = (MA)var1;
        var10000.setBooleanP(((MA)var1).isBooleanP());
        var10000.setByteP(var10001.getByteP());
        var10000.setCharP(var10001.getCharP());
        var10000.setDoubleP(var10001.getDoubleP());
        var10000.setFloatP(var10001.getFloatP());
        var10000.setId(var10001.getId());
        var10000.setIntP(var10001.getIntP());
        var10000.setLongP(var10001.getLongP());
        var10000.setName(var10001.getName());
        var10000.setShortP(var10001.getShortP());
        var10000.setStringP(var10001.getStringP());
    }
}

先不用太介意這個奇葩的類名,先看看這個類生成的代碼在做什么事,它通過生成拷貝屬性值的代碼,來完成我們需要的拷貝邏輯。這個生成類也就是我們前面例子里的copier(BeanCopier copier = BeanCopier.create(MA.class, MA.class, false);)對應的Class。

  • 第二個類
public class BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd extends KeyFactory implements BeanCopierKey {
    // 源類名
    private final String FIELD_0;
    // 目標類名
    private final String FIELD_1;
    // 是否使用Converter
    private final boolean FIELD_2;

    public BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd() {
    }

    public Object newInstance(String var1, String var2, boolean var3) {
        return new BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd(var1, var2, var3);
    }

    public int hashCode() {
        return ((95401 * 54189869 + (this.FIELD_0 != null?this.FIELD_0.hashCode():0)) * 54189869 + (this.FIELD_1 != null?this.FIELD_1.hashCode():0)) * 54189869 + (this.FIELD_2 ^ 1);
    }

    public boolean equals(Object var1) {
          ...
    }

    public String toString() {
        StringBuffer var10000 = new StringBuffer();
        var10000 = (this.FIELD_0 != null?var10000.append(this.FIELD_0.toString()):var10000.append("null")).append(", ");
        return (this.FIELD_1 != null?var10000.append(this.FIELD_1.toString()):var10000.append("null")).append(", ").append(this.FIELD_2).toString();
    }
}

第二個類就不如第一個那么一目了然了,它這就是我們前面講解代碼包結構時,core包中的KeyFactory生成的key,它作為 類一(第一個類) 的唯一標識,在cglib的緩存Map中作為key。

這個類包含一個默認的構造函數、一個newInstance的工廠方法用于創建新的實例,以及重寫的hashCode、equals和toString方法。我們后面會對它進行詳細說明。

在下文中,我們將簡稱 第一個類類一第二個類類二

(2)從BeanCopier#create開始

在瀏覽了生成的類一和類二后,我們從BeanCopier的調用代碼入手。代碼省去了不影響主流程的細節。

// 一次調用
BeanCopier copier = BeanCopier.create(MA.class, MA.class, true);

abstract public class BeanCopier
{
    public static BeanCopier create(Class source, Class target, boolean useConverter) {
        Generator gen = new Generator();
        gen.setSource(source);
        gen.setTarget(target);
        gen.setUseConverter(useConverter);

        // 調用類創建方法
        return gen.create();
    }

    public static class Generator extends AbstractClassGenerator {

        public BeanCopier create() {
            // 1.通過KEY_FACTORY創建key實例
            Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
            // 2.調用AbstractClassGenerator#create創建copy類
            return (BeanCopier)super.create(key);
        }
        ...
    }
}

BeanCopier#create只做了一件事情,新建了一個Generator實例,并調用了Generator#create方法。

BeanCopier$Generator#create就稍微復雜一點了:

  1. 通過KEY_FACTORY創建key實例
  2. 調用AbstractClassGenerator#create創建copy類

這個KEY_FACTORY.newInstance是不是有點眼熟了,我們剛剛提到的 類二 中,就有這個newInstance方法。由此可以猜測,KEY_FACTORY應該是 類二 的一個實例。

(3)KEY_FACTORY的由來

我們留意下KEY_FACTORY在BeanCopier中是如何定義的。

    private static final BeanCopierKey KEY_FACTORY =
      (BeanCopierKey)KeyFactory.create(BeanCopierKey.class);

查看源碼,可以整理出接下來調用鏈路大致如下:

KeyFactory#create

-> KeyFactory$Generator#create

-> AbstractClassGenerator#create

OK,看到了AbstractClassGenerator#create方法,重頭戲來了。

(4)AbstractClassGenerator#create方法流程

這個方法封裝了類創建的主要流程。為了便于閱讀,去掉了部分不重要的代碼。

protected Object create(Object key) {
    Class gen = null;

    synchronized (source) {
        ClassLoader loader = getClassLoader();
        Map cache2 = null;
        cache2 = (Map) source.cache.get(loader);
        
        /** 1.嘗試加載緩存 **/
        // 如果緩存不存在,則新建空的緩存
        if (cache2 == null) {
            cache2 = new HashMap();
            cache2.put(NAME_KEY, new HashSet()); // NAME_KEY對應的Set集合用于去重
            source.cache.put(loader, cache2);
        }
        // 如果緩存存在,且要求使用緩存
        else if (useCache) {
            // 通過key獲取緩存中的生成類(拿到的是引用[WeakReference],調用ref.get()拿到類本身)
            Reference ref = (Reference) cache2.get(key);
            gen = (Class) ((ref == null) ? null : ref.get());
        }

        this.key = key;
        
        /** 2.如果不能從緩存中查找到生成類,則新建類 **/
        if (gen == null) {
            // strategy.generate中調用了子類里的generateClass函數
            // 并返回生成的字節碼
            byte[] b = strategy.generate(this);

            String className = ClassNameReader.getClassName(new ClassReader(b));

            // 將className放入NAME_KEY對應的Set中
            getClassNameCache(loader).add(className);
            
            // 根據返回的字節碼生成類
            gen = ReflectUtils.defineClass(className, b, loader);
        }

        if (useCache) {
            // 在緩存中放入新生成的類
            cache2.put(key, new WeakReference(gen));
        }
        /** 3.根據生成類,創建實例并返回 **/
        return firstInstance(gen);
    }
    /** 3.根據生成類,創建實例并返回 **/
    return firstInstance(gen);
}

結合代碼,分析下具體流程。

  1. 嘗試加載緩存,大致流程代碼已經交代得很清楚,這里簡單介紹幾個實現細節

    • 關于source.cache
      • source.cache用于緩存生成類,是一個兩層嵌套Map,第一層key值為classloader,第二層key值為生成類對應的唯一索引名(在這里就是"BeanCopierKey"啦)
      • source.cache使用了 WeakHashMap
        • WeakHashMap的key值為弱引用(WeakReference)。如果一個WeakHashMap的key被回收,那么它對應用的value也將被自動的被移除。這也是為什么要使用classloader作為key,當classloader被回收,使用這個classloader加載的類也應該被回收,在這時將這個鍵值對移除是合理的。
    • 在第二層Map中,出現了唯一一個不和諧的key值:NAME_KEY。它對應的Set存儲了當前緩存的所有生成類的類名,用于檢測生成類的類名是否重復。
  2. 如果不能從緩存中查找到生成類,則新建類

    • (1) 根據生成策略(GeneratorStrategy)生成字節碼
      • 我們可以看看默認的DefaultGeneratorStrategy是如何實現的


        DefaultGeneratorStrategy
        • 首先,統一調用了子類(KeyFactory)的Generator#generateClass函數,完成了類的構建。也可以看做是完成了對類的定義。我們將在(5)KeyFactory#generateClass方法流程具體說明。
        • 然后,調用DebuggingClassWriter#toByteArray轉為字節碼輸出。關于DebuggingClassWriter,它通過封裝ClassWriter,實現了從定義的類結構到字節碼的轉換工作。
    • (2) 根據字節碼創建類,其原理是通過反射調用ClassLoader#defineClass方法。
    • (3) 如果要求使用緩存,則將新生成的類放入緩存中。
      • 這里有個小問題值得討論下~。我們都知道,GC判斷能否回收這個對象,是檢查當前這個對象是否還被其他對象強引用。因此,代碼里將新生成的類加了一層弱引用后放入緩存中,以保證緩存的引用不影響生成類的釋放。咋一看這是很合理的,但是類的回收和普通對象的回收不太一樣,它要求必須滿足 “加載該類的ClassLoader已經被回收” 的條件才允許將這個類回收。 當滿足這個條件時 ,按照前面的介紹,由于我們的source.cache本就是WeakHashMap,classloader所對應的鍵值對已經被回收,那么生成類是否使用WeakReference已經無所謂了(反正這層引用已經失效了)。
  3. 根據生成類,創建實例并返回

    • firstInstance方法由子類自定義實現,KeyFactory的實現是直接通過反射調用生成類的newInstance方法,設置入參為null。

(5)KeyFactory#generateClass方法流程

#generateClass作為模板方法,由各子類實現,用于自定義子類想要生成的類結構。

        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());
            // 創建class
            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();
        }

還記得我們在一開始提到的 類二 么,它就是靠這個方法定義出來滴~

大致的流程已經可以通過代碼梳理出來,這里就不展開了。

值得提及的細節是流程中類名的生成問題,是通過調用AbstractClassGenerator定義的getClassName()實現:

  1. 其中用于去重的nameCache,是從(4)AbstractClassGenerator#create方法流程中提到的NAME_KEY中獲取。
  2. namingPolicy是命名策略,默認規則(DefaultNamingPolicy中定義)是: 被代理類名 + <span>"$$"</span> + CGLIB核心處理類 + "ByCGLIB" + <span>"$$"</span> + key的hashCode

    代碼如下:
    private String getClassName(final ClassLoader loader) {
        // 獲取現在緩存的所有className
        final Set nameCache = getClassNameCache(loader);

        return namingPolicy.getClassName(namePrefix, source.name, key, new Predicate() {

            // 根據nameCache去重
            public boolean evaluate(Object arg) {
                return nameCache.contains(arg);
            }
        });
    }



PS:之所以要為類二重寫hashCode和equals方法,是因為類二的實例是作為key存在在HashMap中的。

最后,對于代碼中關于ClassEmitter & MethodEmitter是如何封裝ASM的實現,以及它如何配合ClassVisitor(包含ClassWriter/DebuggingClassWriter)實現轉換字節碼等細節,內容較為繁瑣,可以單獨開一篇博文來講啦~。

至此,終于把KEY_FACTORY的創建講完了(可以跳回(3)KEY_FACTORY的由來 瞅瞅)~

還記得大明湖畔的BeanCopier$Generator#create嗎?我們回顧一下。

        public BeanCopier create() {
            // 1.通過KEY_FACTORY創建key實例
            Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
            // 2.調用AbstractClassGenerator#create創建copy類
            return (BeanCopier)super.create(key);
        }

我們已經說過,這個create方法一共做了兩件事情:

  1. 通過KEY_FACTORY創建key實例;
  2. 調用AbstractClassGenerator#create創建copy類。

在前面的流程中,我們已經搞定了第一件事情:成功創建了KEY_FACTORY(生成了 類二 ,并創建了類二的第一個實例),并根據KEY_FACTORY.newInstance方法,我們為即將被創建的copier準備一個唯一的key值了。這里,key的唯一性由三個元素共同決定:源類名、目標類名、以及是否需要使用Converter(可以跳到 (1)CGLIB做了什么 回顧下生成的類二)。

那么,接下來我們將著手第二件事情: 類一 的生成流程。

類一的生成與key的生成類似,也是通過AbstractClassGenerator#create方法完成類生成,因此我們只需要關注其中的變化點,也就是BeanCopier#generateClass。

(6)BeanCopier#generateClass方法流程

按照前面流程的介紹,我們知道generateClass方法就是用于定義類結構的,這里也不例外。

       public void generateClass(ClassVisitor v) {
            Type sourceType = Type.getType(source);
            Type targetType = Type.getType(target);
            ClassEmitter ce = new ClassEmitter(v);

            // 創建class
            ce.begin_class(Constants.V1_2,
                           Constants.ACC_PUBLIC,
                            // 類名,在AbstractClassGenerator#getClassName中生成
                           getClassName(),
                           BEAN_COPIER,
                           null,
                           Constants.SOURCE_FILE);

            // 生成默認構造函數
            EmitUtils.null_constructor(ce);

            // [BEGIN COPY METHOD]生成copy方法
            CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
            PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source);
            PropertyDescriptor[] setters = ReflectUtils.getBeanSetters(target);

            // names可根據屬性名查找對應的getter方法
            Map names = new HashMap();
            for (int i = 0; i < getters.length; i++) {
                names.put(getters[i].getName(), getters[i]);
            }

            Local targetLocal = e.make_local();
            Local sourceLocal = e.make_local();
            if (useConverter) {
                e.load_arg(1);
                e.checkcast(targetType);
                e.store_local(targetLocal);
                e.load_arg(0);                
                e.checkcast(sourceType);
                e.store_local(sourceLocal);
            } else {
                e.load_arg(1);
                e.checkcast(targetType);
                e.load_arg(0);
                e.checkcast(sourceType);
            }
            // 為每個屬性生成賦值語句
            for (int i = 0; i < setters.length; i++) {
                PropertyDescriptor setter = setters[i];
                PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName());

                if (getter != null) {
                    MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
                    MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod());
                    if (useConverter) {
                        Type setterType = write.getSignature().getArgumentTypes()[0];
                        e.load_local(targetLocal);
                        e.load_arg(2);
                        e.load_local(sourceLocal);
                        e.invoke(read);
                        e.box(read.getSignature().getReturnType());
                        EmitUtils.load_class(e, setterType);
                        e.push(write.getSignature().getName());
                        e.invoke_interface(CONVERTER, CONVERT);
                        e.unbox_or_zero(setterType);
                        e.invoke(write);
                        // 如果property類型相同
                    } else if (compatible(getter, setter)) {
                        e.dup2();
                        e.invoke(read);
                        e.invoke(write);
                    }
                }
            }
            e.return_value();
            e.end_method();
            //[END COPY METHOD]

            ce.end_class();
        }

通過BeanCopier$Generator#generateClass方法,我們得到了類一的完整定義,并為它定義了相應的copy方法。
接下來就是繼續(4)AbstractClassGenerator#create方法流程的老路,通過反射獲取類一的實例,也就是前面代碼里的copier。剩下的事情,只需要調用copier.copy(source, target, null),就可以完成source bean到target bean的屬性拷貝了。

一次BeanCopier#create的調用流程也大功告成~。

更多細節

  1. 我們注意到KeyFactory和BeanCopier都有一個繼承自AbstractClassGenerator的名為Generator的內部類,而AbstractClassGenerator封裝了類創建的主要流程,只需要將 “想要創建一個啥樣的類” 這段邏輯,提取出來封裝為模板方法 generateClass ,供繼承類實現。同樣的,BulkCopier、BeanGenerator、Enhancer等類也是如此實現。
  2. BeanCopier#create為什么要返回一個生成類的實例,而不是直接返回生成類。
    • 應該是因為如果直接返回生成類,調用方還得用反射調用構造函數獲得實例,在使用上不如直接返回生成類的實例方便。
  3. 為什么在BeanCopier中,需要專門為key值生成一個對象。
    • 因為有些生成類需要multi-vaules key來標識這個生成類。比如我們的copier,需要“源類名、目標類名、以及是否需要使用Converter”三個因素共同保證這個生成類的唯一性。

Tips

  1. BeanCopier流程雖然比較簡單,但分析流程中已經涉及到對關鍵類AbstractClassGenerator、KeyFactory、GeneratorStrategy、NamingPolicy等的使用,基于此可以輕松閱讀后續的BulkBean、BeanGenerator等類。Enhancer&FastClass的實現要更特殊一些,后續將作進一步介紹。
  2. 關于如何查看生成的class文件,在代碼里加入
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "文件路徑");
  3. 需要自行驗證的地方,結合CGLIB提供的測試代碼跑一下即可。
    • 文中代碼版本為cglib-RELEASE_3_2_0

參考文獻

  1. http://www.javacodegeeks.com/2013/12/cglib-the-missing-manual.html
  2. http://www.iteye.com/topic/799827
  3. http://www.cnblogs.com/cruze/p/3843996.html
  4. http://cglib.sourceforge.net/apidocs/index.html
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容