避開類本身的構造函數

背景

最近在用XStream,轉換XML到Java對象時,發現一個奇怪的現象。Java對象中,在構造函數中取的一些默認值,及在字段定義時賦值的初值,這些值由于有初值,在XML里并沒有映射。結果在XStream反序列化之后,還是null,只有在XML中出現的值才會被賦值。

問題提出

一開始我以為是XStream的能力,它會把XML中沒有的字段找出來,然后設置成null。為了證明,然后我在set方法上設置了斷點,但是發現代碼并沒有進去。我又懷疑是直接反射的變量。然后我又在構造函數里及字段的賦初值的地方設置了斷點,結果發現,依然沒有中斷。
難道,這個對象的構造,沒有調用構造函數?

分析問題

為了搞清楚這個問題,單步一下代碼,從new XStream(new DomDriver());開始。發現當ReflectionProvider reflectionProvider沒有傳入時,會使用JVM::bestReflectionProvider()方法,然后返回了Sun14ReflectionProvider
繼續深入,發現如下代碼:


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

解析

  1. 反序列化使用Sun14ReflectionProvider::getMungedConstructor()方法取構造函數。
  2. 在這里reflectionFactory.newConstructorForSerialization(type, Object.class.getDeclaredConstructor(new Class[0]));傳入了目標類型,及Object類型的默認構造函數。
  3. 然后在Sun14ReflectionProvider::newConstructorForSerialization()中用Object類型的構造函數,產生了目標類型的構造函數。

在函數在sun的API中已經提供,Sun14ReflectionProvider::newConstructorForSerialization()分析:

  1. (new MethodAccessorGenerator()).generateSerializationConstructor 返回了SerializationConstructorAccessorImpl對象,用于Object構造函數可以訪問目標類型。
  2. this.newConstructor來新產生Object類型的構造函數。此函數是新生產出來的,和在函數區已有的構造函數不是同一個。
  3. this.setConstructorAccessor(var4, var3);最后把兩者關聯。

最后:幾種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;
    }
  1. Sun14ReflectionProvider是最優的的反序列化器。
  2. Sun14ReflectionProvider反序列化器,Sun, Apple, IBM等各大JVM都有實現,并且版本高于JDK1.4。
  3. Sun14ReflectionProvider反序列化器內部使用的用是sun提供的反射API。
  4. Harmony是Apache Harmony項目,一個開源的JVM虛擬機。
  5. 使用XStream時改為new XStream(new PureJavaReflectionProvider(), new DomDriver());,可以解決此問題,使用類的正常構造函數。

結論

  1. 類在構造時,可以避開自己的構造函數,而合用Object的構造函數。
  2. 類字段的初始值的賦值,在編譯時會編譯到其構造函數中。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,837評論 18 139
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,739評論 18 399
  • 前言 人生苦多,快來 Kotlin ,快速學習Kotlin! 什么是Kotlin? Kotlin 是種靜態類型編程...
    任半生囂狂閱讀 26,252評論 9 118
  • 從三月份找實習到現在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發崗...
    時芥藍閱讀 42,340評論 11 349
  • 經常,讀了關于愛情的文章之后我總會覺得很孤獨。因為是單身久了,看到了情侶的甜蜜和單身者的自白我都會被觸動。我覺得我...
    灑落的陽光閱讀 101評論 0 1