背景
最近在用XStream线罕,轉(zhuǎn)換XML到Java對(duì)象時(shí),發(fā)現(xiàn)一個(gè)奇怪的現(xiàn)象般哼。Java對(duì)象中吴汪,在構(gòu)造函數(shù)中取的一些默認(rèn)值,及在字段定義時(shí)賦值的初值蒸眠,這些值由于有初值漾橙,在XML里并沒有映射。結(jié)果在XStream反序列化之后楞卡,還是null霜运,只有在XML中出現(xiàn)的值才會(huì)被賦值。
問題提出
一開始我以為是XStream的能力蒋腮,它會(huì)把XML中沒有的字段找出來淘捡,然后設(shè)置成null。為了證明池摧,然后我在set方法上設(shè)置了斷點(diǎn)焦除,但是發(fā)現(xiàn)代碼并沒有進(jìn)去。我又懷疑是直接反射的變量作彤。然后我又在構(gòu)造函數(shù)里及字段的賦初值的地方設(shè)置了斷點(diǎn)膘魄,結(jié)果發(fā)現(xiàn),依然沒有中斷竭讳。
難道创葡,這個(gè)對(duì)象的構(gòu)造,沒有調(diào)用構(gòu)造函數(shù)绢慢?
分析問題
為了搞清楚這個(gè)問題蹈丸,單步一下代碼,從new XStream(new DomDriver());開始。發(fā)現(xiàn)當(dāng)ReflectionProvider reflectionProvider沒有傳入時(shí)逻杖,會(huì)使用JVM::bestReflectionProvider()方法奋岁,然后返回了Sun14ReflectionProvider
繼續(xù)深入,發(fā)現(xiàn)如下代碼:
private Constructor getMungedConstructor(Class type) throws NoSuchMethodException {
final WeakReference ref = (WeakReference)constructorCache.get(type);
Constructor ctor = (Constructor)(ref == null ? null : ref.get());
if (ctor == null) {
ctor = reflectionFactory.newConstructorForSerialization(type, Object.class.getDeclaredConstructor(new Class[0]));
constructorCache.put(type, new WeakReference(ctor));
}
return ctor;
}
// in ReflectionFactory
public Constructor newConstructorForSerialization(Class var1, Constructor var2) {
if(var2.getDeclaringClass() == var1) {
return var2;
} else {
SerializationConstructorAccessorImpl var3 = (new MethodAccessorGenerator()).generateSerializationConstructor(var1, var2.getParameterTypes(), var2.getExceptionTypes(), var2.getModifiers(), var2.getDeclaringClass());
Constructor var4 = this.newConstructor(var2.getDeclaringClass(), var2.getParameterTypes(), var2.getExceptionTypes(), var2.getModifiers(), langReflectAccess().getConstructorSlot(var2), langReflectAccess().getConstructorSignature(var2), langReflectAccess().getConstructorAnnotations(var2), langReflectAccess().getConstructorParameterAnnotations(var2));
this.setConstructorAccessor(var4, var3);
return var4;
}
}
解析
- 反序列化使用
Sun14ReflectionProvider::getMungedConstructor()
方法取構(gòu)造函數(shù)荸百。 - 在這里
reflectionFactory.newConstructorForSerialization(type, Object.class.getDeclaredConstructor(new Class[0]));
傳入了目標(biāo)類型闻伶,及Object類型的默認(rèn)構(gòu)造函數(shù)。 - 然后在
Sun14ReflectionProvider::newConstructorForSerialization()
中用Object類型的構(gòu)造函數(shù)够话,產(chǎn)生了目標(biāo)類型的構(gòu)造函數(shù)蓝翰。
在函數(shù)在sun的API中已經(jīng)提供,Sun14ReflectionProvider::newConstructorForSerialization()
分析:
-
(new MethodAccessorGenerator()).generateSerializationConstructor
返回了SerializationConstructorAccessorImpl對(duì)象女嘲,用于Object構(gòu)造函數(shù)可以訪問目標(biāo)類型畜份。 -
this.newConstructor
來新產(chǎn)生Object類型的構(gòu)造函數(shù)。此函數(shù)是新生產(chǎn)出來的欣尼,和在函數(shù)區(qū)已有的構(gòu)造函數(shù)不是同一個(gè)爆雹。 -
this.setConstructorAccessor(var4, var3);
最后把兩者關(guān)聯(lián)。
最后:幾種ReflectionProvider
public synchronized ReflectionProvider bestReflectionProvider() {
if (reflectionProvider == null) {
try {
if ( canUseSun14ReflectionProvider() ) {
String cls = "com.thoughtworks.xstream.converters.reflection.Sun14ReflectionProvider";
reflectionProvider = (ReflectionProvider) loadClass(cls).newInstance();
} else if (canUseHarmonyReflectionProvider()) { // call isHarmony()
String cls = "com.thoughtworks.xstream.converters.reflection.HarmonyReflectionProvider";
reflectionProvider = (ReflectionProvider) loadClass(cls).newInstance();
}
if (reflectionProvider == null) {
reflectionProvider = new PureJavaReflectionProvider();
}
} //...
}
return reflectionProvider;
}
private boolean canUseSun14ReflectionProvider() {
return (isSun()
|| isApple()
|| isHPUX()
|| isIBM()
|| isBlackdown()
|| isBEAWithUnsafeSupport()
|| isHitachi()
|| isSAP()
|| isDiablo())
&& is14()
&& loadClass("sun.misc.Unsafe") != null;
}
private static boolean isHarmony() {
return vendor.indexOf("Apache Software Foundation") != -1;
}
- Sun14ReflectionProvider是最優(yōu)的的反序列化器愕鼓。
- Sun14ReflectionProvider反序列化器钙态,Sun, Apple, IBM等各大JVM都有實(shí)現(xiàn),并且版本高于JDK1.4菇晃。
- Sun14ReflectionProvider反序列化器內(nèi)部使用的用是sun提供的反射API册倒。
- Harmony是Apache Harmony項(xiàng)目,一個(gè)開源的JVM虛擬機(jī)磺送。
- 使用XStream時(shí)改為
new XStream(new PureJavaReflectionProvider(), new DomDriver());
驻子,可以解決此問題,使用類的正常構(gòu)造函數(shù)估灿。
結(jié)論
- 類在構(gòu)造時(shí)崇呵,可以避開自己的構(gòu)造函數(shù),而合用Object的構(gòu)造函數(shù)甲捏。
- 類字段的初始值的賦值,在編譯時(shí)會(huì)編譯到其構(gòu)造函數(shù)中鞭执。