本文將簡要介紹CGLIB代碼包結(jié)構(gòu)以及核心類的基本功能及汉,然后通過介紹BeanCopier的使用例子陶缺,將其作為引子對相關(guān)源碼實現(xiàn)進(jìn)行分析。
CGLIB代碼包結(jié)構(gòu)
1.core
- ClassGenerator(接口) & AbstractClassGenerator(實現(xiàn)類)
- 作為cglib代碼中 最核心的調(diào)度者 舔株,封裝了類創(chuàng)建的主要流程莺琳,并支持一些緩存操作、命名策略(NamingPolicy)载慈、代碼生成策略(GeneratorStrategy)等惭等。
- 其中,protected Object create(Object key) 作為模版方法办铡,定義了類的生成過程辞做。同時將變化點進(jìn)行封裝,供繼承類自主實現(xiàn)寡具。
- GeneratorStrategy(接口) & DefaultGeneratorStrategy(默認(rèn)實現(xiàn)類)
- 控制ClassGenerator生成class的字節(jié)碼秤茅。
- 為繼承類預(yù)留了兩個抽象方法,可以對生成的字節(jié)碼進(jìn)行操作童叠。
- NamingPolicy(接口) & DefaultNamingPolicy(默認(rèn)實現(xiàn)類)
- 用于控制生成類的命名規(guī)則框喳。
- 一般的命名規(guī)則:
- 被代理類名 + $$ + CGLIB核心處理類 + "ByCGLIB" + $$ + key的hashCode。
- 示例:FastSource<span>$$</span>FastClassByCGLIB<span>$$</span>e1a36bab.class。
- KeyFactory
- 每個生成類都會在cglib的緩存中存在唯一的key與之對應(yīng)五垮,這個key就通過KeyFactory進(jìn)行生成乍惊。
- DebuggingClassWriter
- 被DefaultGeneratorStrategy調(diào)用,將生成類轉(zhuǎn)為字節(jié)碼輸出。
- 將生成的字節(jié)碼寫入到文件中(debugLocation)放仗。
- ClassEmitter & CodeEmitter
- 封裝了ASM的實現(xiàn)润绎,提供對類和方法的字節(jié)碼操作。
- 工具類
- EmitUtils:封裝了一些字節(jié)碼操作的基本函數(shù)匙监。
- ReflectUtils :封裝JDK中的反射操作凡橱。
2.beans
- BeanCopier:用于兩個bean之間,同名屬性間的拷貝亭姥。
- BulkBean:用于兩個bean之間稼钩,自定義get&set方法間的拷貝频敛。
- BeanMap:針對POJO Bean與Map對象間的拷貝裕坊。
- BeanGenerator:根據(jù)Map<String,Class>properties的屬性定義,動態(tài)生成POJO Bean類牵素。
- ImmutableBean:同樣用于兩個bean間的屬性拷貝粮揉,但生成的bean不允許調(diào)用set方法巡李,也就是說,生成的對象是不可變的扶认。
3.reflect
- FastClass & FastMethod
- FastClass機(jī)制就是對一個類的方法建立索引侨拦,通過索引來直接調(diào)用相應(yīng)的方法.
4.proxy
- Enhancer: 用于生成動態(tài)代理類
此處略過了部分與本文無關(guān)的類,重點關(guān)注core包中的類即可~
更多細(xì)節(jié)可以參考這里辐宾。
BeanCopier實現(xiàn)機(jī)制
1.BeanCopier的使用
顧名思義狱从,BeanCopier是用于在兩個bean之間進(jìn)行屬性拷貝的。BeanCopier支持兩種方式叠纹,一種是不使用Converter的方式季研,僅對兩個bean間屬性名和類型完全相同的變量進(jìn)行拷貝。另一種則引入Converter誉察,可以對某些特定屬性值進(jìn)行特殊操作与涡。
代碼如下所示。
不使用Converter的例子
public void testSimple() {
// 動態(tài)生成用于復(fù)制的類,false為不使用Converter類
BeanCopier copier = BeanCopier.create(MA.class, MA.class, false);
MA source = new MA();
source.setIntP(42);
MA target = new MA();
// 執(zhí)行source到target的屬性復(fù)制
copier.copy(source, target, null);
assertTrue(target.getIntP() == 42);
}
使用Converter的例子
public void testConvert() {
// 動態(tài)生成用于復(fù)制的類,并使用Converter類
BeanCopier copier = BeanCopier.create(MA.class, MA.class, true);
MA source = new MA();
source.setIntP(42);
MA target = new MA();
// 執(zhí)行source到target的屬性復(fù)制
copier.copy(source, target, new Converter() {
/**
* @param sourceValue source對象屬性值
* @param targetClass target對象對應(yīng)類
* @param methodName targetClass里屬性對應(yīng)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間進(jìn)行復(fù)制的類
copier.copy(source, target, converter) // 執(zhí)行復(fù)制
2.性能分析
場景 | 耗時(1000000次調(diào)用) | 原理 |
---|---|---|
直接使用get&set方法 | 22ms | 直接調(diào)用 |
使用BeanCopiers(不使用Converter) | 22ms | 修改字節(jié)碼 |
使用BeanCopiers(使用Converter) | 249ms | 修改字節(jié)碼 |
使用BeanUtils | 12983ms | 反射 |
使用PropertyUtils(不使用Converter) | 3922ms | 反射 |
從上面數(shù)據(jù)可以看出持偏,不使用Converter時驼卖,BeanCopiers與直接調(diào)用get&set方法性能相當(dāng)。
3.一次調(diào)用流程
接下來鸿秆,我們來看看在簡單的調(diào)用背后款慨,cglib替我們做了哪些事。
(1)CGLIB做了什么
CGLIB的核心在于通過操作字節(jié)碼生成類谬莹,來實現(xiàn)原本需要通過反射或者一堆代碼才能實現(xiàn)的邏輯檩奠。在我們剛剛的例子里(注意桩了, 是不帶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);
)對應(yīng)的Class。
- 第二個類
public class BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd extends KeyFactory implements BeanCopierKey {
// 源類名
private final String FIELD_0;
// 目標(biāo)類名
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();
}
}
第二個類就不如第一個那么一目了然了蛮寂,它這就是我們前面講解代碼包結(jié)構(gòu)時蔽午,core包中的KeyFactory生成的key,它作為 類一(第一個類) 的唯一標(biāo)識酬蹋,在cglib的緩存Map中作為key及老。
這個類包含一個默認(rèn)的構(gòu)造函數(shù)、一個newInstance的工廠方法用于創(chuàng)建新的實例范抓,以及重寫的hashCode骄恶、equals和toString方法。我們后面會對它進(jìn)行詳細(xì)說明匕垫。
在下文中僧鲁,我們將簡稱 第一個類 為 類一 , 第二個類 為 類二象泵。
(2)從BeanCopier#create開始
在瀏覽了生成的類一和類二后寞秃,我們從BeanCopier的調(diào)用代碼入手。代碼省去了不影響主流程的細(xì)節(jié)单芜。
// 一次調(diào)用
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);
// 調(diào)用類創(chuàng)建方法
return gen.create();
}
public static class Generator extends AbstractClassGenerator {
public BeanCopier create() {
// 1.通過KEY_FACTORY創(chuàng)建key實例
Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
// 2.調(diào)用AbstractClassGenerator#create創(chuàng)建copy類
return (BeanCopier)super.create(key);
}
...
}
}
BeanCopier#create只做了一件事情,新建了一個Generator實例犁柜,并調(diào)用了Generator#create方法洲鸠。
BeanCopier$Generator#create就稍微復(fù)雜一點了:
- 通過KEY_FACTORY創(chuàng)建key實例
- 調(diào)用AbstractClassGenerator#create創(chuàng)建copy類
這個KEY_FACTORY.newInstance是不是有點眼熟了,我們剛剛提到的 類二 中馋缅,就有這個newInstance方法扒腕。由此可以猜測,KEY_FACTORY應(yīng)該是 類二 的一個實例萤悴。
(3)KEY_FACTORY的由來
我們留意下KEY_FACTORY在BeanCopier中是如何定義的瘾腰。
private static final BeanCopierKey KEY_FACTORY =
(BeanCopierKey)KeyFactory.create(BeanCopierKey.class);
查看源碼,可以整理出接下來調(diào)用鏈路大致如下:
KeyFactory#create
-> KeyFactory$Generator#create
-> AbstractClassGenerator#create
OK覆履,看到了AbstractClassGenerator#create方法蹋盆,重頭戲來了费薄。
(4)AbstractClassGenerator#create方法流程
這個方法封裝了類創(chuàng)建的主要流程。為了便于閱讀栖雾,去掉了部分不重要的代碼楞抡。
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對應(yīng)的Set集合用于去重
source.cache.put(loader, cache2);
}
// 如果緩存存在,且要求使用緩存
else if (useCache) {
// 通過key獲取緩存中的生成類(拿到的是引用[WeakReference],調(diào)用ref.get()拿到類本身)
Reference ref = (Reference) cache2.get(key);
gen = (Class) ((ref == null) ? null : ref.get());
}
this.key = key;
/** 2.如果不能從緩存中查找到生成類,則新建類 **/
if (gen == null) {
// strategy.generate中調(diào)用了子類里的generateClass函數(shù)
// 并返回生成的字節(jié)碼
byte[] b = strategy.generate(this);
String className = ClassNameReader.getClassName(new ClassReader(b));
// 將className放入NAME_KEY對應(yīng)的Set中
getClassNameCache(loader).add(className);
// 根據(jù)返回的字節(jié)碼生成類
gen = ReflectUtils.defineClass(className, b, loader);
}
if (useCache) {
// 在緩存中放入新生成的類
cache2.put(key, new WeakReference(gen));
}
/** 3.根據(jù)生成類,創(chuàng)建實例并返回 **/
return firstInstance(gen);
}
/** 3.根據(jù)生成類析藕,創(chuàng)建實例并返回 **/
return firstInstance(gen);
}
結(jié)合代碼召廷,分析下具體流程。
-
嘗試加載緩存账胧,大致流程代碼已經(jīng)交代得很清楚竞慢,這里簡單介紹幾個實現(xiàn)細(xì)節(jié)
- 關(guān)于source.cache
- source.cache用于緩存生成類,是一個兩層嵌套Map治泥,第一層key值為classloader筹煮,第二層key值為生成類對應(yīng)的唯一索引名(在這里就是"BeanCopierKey"啦)
- source.cache使用了 WeakHashMap
- WeakHashMap的key值為弱引用(WeakReference)。如果一個WeakHashMap的key被回收车摄,那么它對應(yīng)用的value也將被自動的被移除寺谤。這也是為什么要使用classloader作為key,當(dāng)classloader被回收吮播,使用這個classloader加載的類也應(yīng)該被回收变屁,在這時將這個鍵值對移除是合理的。
- 在第二層Map中意狠,出現(xiàn)了唯一一個不和諧的key值:NAME_KEY粟关。它對應(yīng)的Set存儲了當(dāng)前緩存的所有生成類的類名,用于檢測生成類的類名是否重復(fù)环戈。
- 關(guān)于source.cache
-
如果不能從緩存中查找到生成類,則新建類
- (1) 根據(jù)生成策略(GeneratorStrategy)生成字節(jié)碼
-
我們可以看看默認(rèn)的DefaultGeneratorStrategy是如何實現(xiàn)的
DefaultGeneratorStrategy- 首先闷板,統(tǒng)一調(diào)用了子類(KeyFactory)的Generator#generateClass函數(shù),完成了類的構(gòu)建院塞。也可以看做是完成了對類的定義遮晚。我們將在(5)KeyFactory#generateClass方法流程具體說明。
- 然后拦止,調(diào)用DebuggingClassWriter#toByteArray轉(zhuǎn)為字節(jié)碼輸出县遣。關(guān)于DebuggingClassWriter,它通過封裝ClassWriter汹族,實現(xiàn)了從定義的類結(jié)構(gòu)到字節(jié)碼的轉(zhuǎn)換工作萧求。
-
- (2) 根據(jù)字節(jié)碼創(chuàng)建類,其原理是通過反射調(diào)用ClassLoader#defineClass方法。
- (3) 如果要求使用緩存顶瞒,則將新生成的類放入緩存中夸政。
- 這里有個小問題值得討論下~。我們都知道榴徐,GC判斷能否回收這個對象守问,是檢查當(dāng)前這個對象是否還被其他對象強(qiáng)引用匀归。因此,代碼里將新生成的類加了一層弱引用后放入緩存中酪碘,以保證緩存的引用不影響生成類的釋放朋譬。咋一看這是很合理的,但是類的回收和普通對象的回收不太一樣兴垦,它要求必須滿足 “加載該類的ClassLoader已經(jīng)被回收” 的條件才允許將這個類回收徙赢。 當(dāng)滿足這個條件時 ,按照前面的介紹探越,由于我們的source.cache本就是WeakHashMap狡赐,classloader所對應(yīng)的鍵值對已經(jīng)被回收,那么生成類是否使用WeakReference已經(jīng)無所謂了(反正這層引用已經(jīng)失效了)钦幔。
- (1) 根據(jù)生成策略(GeneratorStrategy)生成字節(jié)碼
-
根據(jù)生成類枕屉,創(chuàng)建實例并返回
- firstInstance方法由子類自定義實現(xiàn),KeyFactory的實現(xiàn)是直接通過反射調(diào)用生成類的newInstance方法鲤氢,設(shè)置入?yún)閚ull搀擂。
(5)KeyFactory#generateClass方法流程
#generateClass作為模板方法,由各子類實現(xiàn)卷玉,用于自定義子類想要生成的類結(jié)構(gòu)哨颂。
public void generateClass(ClassVisitor v) {
ClassEmitter ce = new ClassEmitter(v);
// 對定義key工廠類結(jié)構(gòu)的接口進(jìn)行判斷,判斷該接口是否只有newInstance一個方法相种,newInstance的返回值是否為Object
Method newInstance = ReflectUtils.findNewInstance(keyInterface);
if (!newInstance.getReturnType().equals(Object.class)) {
throw new IllegalArgumentException("newInstance method must return Object");
}
// 獲取newInstance的入?yún)㈩愋屯眨颂幨褂肁SM的Type來定義
Type[] parameterTypes = TypeUtils.getTypes(newInstance.getParameterTypes());
// 創(chuàng)建class
ce.begin_class(Constants.V1_2,
Constants.ACC_PUBLIC,
getClassName(),
KEY_FACTORY,
new Type[]{ Type.getType(keyInterface) },
Constants.SOURCE_FILE);
//生成默認(rèn)構(gòu)造函數(shù)
EmitUtils.null_constructor(ce);
//生成newInstance 工廠方法
EmitUtils.factory_method(ce, ReflectUtils.getSignature(newInstance));
//生成有參構(gòu)造方法
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();
//為每一個入?yún)⑸梢粋€相同類型的類字段
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函數(shù)
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函數(shù),在equals函數(shù)中對每個入?yún)⒍歼M(jìn)行判斷
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();
}
還記得我們在一開始提到的 類二 么寝并,它就是靠這個方法定義出來滴~
大致的流程已經(jīng)可以通過代碼梳理出來,這里就不展開了箫措。
值得提及的細(xì)節(jié)是流程中類名的生成問題,是通過調(diào)用AbstractClassGenerator定義的getClassName()實現(xiàn):
- 其中用于去重的nameCache衬潦,是從(4)AbstractClassGenerator#create方法流程中提到的NAME_KEY中獲取斤蔓。
- namingPolicy是命名策略,默認(rèn)規(guī)則(DefaultNamingPolicy中定義)是: 被代理類名 + <span>"$$"</span> + CGLIB核心處理類 + "ByCGLIB" + <span>"$$"</span> + key的hashCode
代碼如下:
private String getClassName(final ClassLoader loader) {
// 獲取現(xiàn)在緩存的所有className
final Set nameCache = getClassNameCache(loader);
return namingPolicy.getClassName(namePrefix, source.name, key, new Predicate() {
// 根據(jù)nameCache去重
public boolean evaluate(Object arg) {
return nameCache.contains(arg);
}
});
}
PS:之所以要為類二重寫hashCode和equals方法镀岛,是因為類二的實例是作為key存在在HashMap中的弦牡。
最后,對于代碼中關(guān)于ClassEmitter & MethodEmitter是如何封裝ASM的實現(xiàn)哎媚,以及它如何配合ClassVisitor(包含ClassWriter/DebuggingClassWriter)實現(xiàn)轉(zhuǎn)換字節(jié)碼等細(xì)節(jié)喇伯,內(nèi)容較為繁瑣喊儡,可以單獨(dú)開一篇博文來講啦~拨与。
至此,終于把KEY_FACTORY的創(chuàng)建講完了(可以跳回(3)KEY_FACTORY的由來 瞅瞅)~
還記得大明湖畔的BeanCopier$Generator#create嗎艾猜?我們回顧一下买喧。
public BeanCopier create() {
// 1.通過KEY_FACTORY創(chuàng)建key實例
Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
// 2.調(diào)用AbstractClassGenerator#create創(chuàng)建copy類
return (BeanCopier)super.create(key);
}
我們已經(jīng)說過捻悯,這個create方法一共做了兩件事情:
- 通過KEY_FACTORY創(chuàng)建key實例;
- 調(diào)用AbstractClassGenerator#create創(chuàng)建copy類淤毛。
在前面的流程中今缚,我們已經(jīng)搞定了第一件事情:成功創(chuàng)建了KEY_FACTORY(生成了 類二 ,并創(chuàng)建了類二的第一個實例)低淡,并根據(jù)KEY_FACTORY.newInstance方法姓言,我們?yōu)榧磳⒈粍?chuàng)建的copier準(zhǔn)備一個唯一的key值了。這里蔗蹋,key的唯一性由三個元素共同決定:源類名何荚、目標(biāo)類名、以及是否需要使用Converter(可以跳到 (1)CGLIB做了什么 回顧下生成的類二)猪杭。
那么餐塘,接下來我們將著手第二件事情: 類一 的生成流程。
類一的生成與key的生成類似皂吮,也是通過AbstractClassGenerator#create方法完成類生成戒傻,因此我們只需要關(guān)注其中的變化點,也就是BeanCopier#generateClass蜂筹。
(6)BeanCopier#generateClass方法流程
按照前面流程的介紹需纳,我們知道generateClass方法就是用于定義類結(jié)構(gòu)的,這里也不例外狂票。
public void generateClass(ClassVisitor v) {
Type sourceType = Type.getType(source);
Type targetType = Type.getType(target);
ClassEmitter ce = new ClassEmitter(v);
// 創(chuàng)建class
ce.begin_class(Constants.V1_2,
Constants.ACC_PUBLIC,
// 類名,在AbstractClassGenerator#getClassName中生成
getClassName(),
BEAN_COPIER,
null,
Constants.SOURCE_FILE);
// 生成默認(rèn)構(gòu)造函數(shù)
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可根據(jù)屬性名查找對應(yīng)的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方法候齿,我們得到了類一的完整定義,并為它定義了相應(yīng)的copy方法闺属。
接下來就是繼續(xù)(4)AbstractClassGenerator#create方法流程的老路慌盯,通過反射獲取類一的實例,也就是前面代碼里的copier掂器。剩下的事情亚皂,只需要調(diào)用copier.copy(source, target, null)
,就可以完成source bean到target bean的屬性拷貝了国瓮。
一次BeanCopier#create的調(diào)用流程也大功告成~灭必。
更多細(xì)節(jié)
- 我們注意到KeyFactory和BeanCopier都有一個繼承自AbstractClassGenerator的名為Generator的內(nèi)部類,而AbstractClassGenerator封裝了類創(chuàng)建的主要流程乃摹,只需要將 “想要創(chuàng)建一個啥樣的類” 這段邏輯禁漓,提取出來封裝為模板方法 generateClass ,供繼承類實現(xiàn)孵睬。同樣的播歼,BulkCopier、BeanGenerator掰读、Enhancer等類也是如此實現(xiàn)秘狞。
- BeanCopier#create為什么要返回一個生成類的實例叭莫,而不是直接返回生成類。
- 應(yīng)該是因為如果直接返回生成類烁试,調(diào)用方還得用反射調(diào)用構(gòu)造函數(shù)獲得實例雇初,在使用上不如直接返回生成類的實例方便。
- 為什么在BeanCopier中减响,需要專門為key值生成一個對象靖诗。
- 因為有些生成類需要multi-vaules key來標(biāo)識這個生成類。比如我們的copier支示,需要“源類名呻畸、目標(biāo)類名、以及是否需要使用Converter”三個因素共同保證這個生成類的唯一性悼院。
Tips
- BeanCopier流程雖然比較簡單伤为,但分析流程中已經(jīng)涉及到對關(guān)鍵類AbstractClassGenerator、KeyFactory据途、GeneratorStrategy、NamingPolicy等的使用颖医,基于此可以輕松閱讀后續(xù)的BulkBean位衩、BeanGenerator等類。Enhancer&FastClass的實現(xiàn)要更特殊一些熔萧,后續(xù)將作進(jìn)一步介紹糖驴。
- 關(guān)于如何查看生成的class文件,在代碼里加入
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "文件路徑");
- 需要自行驗證的地方佛致,結(jié)合CGLIB提供的測試代碼跑一下即可贮缕。
- 文中代碼版本為cglib-RELEASE_3_2_0