Gson踩坑筆記:為什么對(duì)象的構(gòu)造方法沒(méi)有被執(zhí)行?

前言

最近做項(xiàng)目遇到了一個(gè)很奇怪的問(wèn)題,情況如下:

創(chuàng)建對(duì)象TestBean,其中type和name需要接口返回并解析,time字段需要客戶(hù)端修改,做一些必要的記錄,希望time的默認(rèn)值為10:

val jsonStr ="{type: 99, name:\"superman\"}"

data class TestBean(val type: Int, val name: String, var time: Long = 10)

在運(yùn)行前,我認(rèn)為這段代碼非常完美,但是結(jié)果卻很意外:


在這里插入圖片描述

難道Gson把構(gòu)造方法中的time設(shè)置成0了嗎?,再次修改代碼:

data class TestBean(val type: Int, val name: String) {

    var time: Long = 10

    override fun toString(): String {
        return "type:$type, name:$name, time:$time"
    }
}

默認(rèn)情況下,data class的toString方法只會(huì)打印構(gòu)造方法中的屬性,所以還需要重寫(xiě)一下toString。我把time屬性從構(gòu)造方法中移出,這次應(yīng)該是穩(wěn)得一匹了吧:


在這里插入圖片描述

what???,time的值并不是10,雖然沒(méi)有找到原因,但是我還可以再改,男人不可以說(shuō)不行:

data class TestBean(val type: Int, val name: String) {

    var time: Long = 10

    init {
        time = 10
        Log.e("lzp", "TestBean create")
    }

    override fun toString(): String {
        return "type:$type, name:$name, time:$time"
    }
}

這一次,我在init方法中,手動(dòng)設(shè)置time=10,并且輸出日志,對(duì)象創(chuàng)建時(shí)一定會(huì)執(zhí)行init方法,絕對(duì)ojbk:


在這里插入圖片描述

init方法也沒(méi)執(zhí)行???好的,讓我靜下心來(lái)仔細(xì)的找到問(wèn)題到底出現(xiàn)在哪里。

正文

經(jīng)過(guò)剛才的踩坑,可以肯定的是,Gson沒(méi)有調(diào)用TestBean的構(gòu)造方法。那么他是怎么創(chuàng)建TestBean的呢?一般來(lái)說(shuō)創(chuàng)建對(duì)象有以下幾種方式:

調(diào)用構(gòu)造方法:TestBean() (new關(guān)鍵字或者反射最終都是使用了構(gòu)造方法)
復(fù)制:Object.copy()

還是要從Gson的源碼去分析這個(gè)問(wèn)題:

val testBean = Gson().fromJson<TestBean>(jsonStr, TestBean::class.java)

我們對(duì)fromJson方法進(jìn)行追蹤,發(fā)現(xiàn)fromJson重寫(xiě)了好幾個(gè),最終會(huì)定位到:

 public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
    boolean isEmpty = true;
    boolean oldLenient = reader.isLenient();
    reader.setLenient(true);
    try {
      reader.peek();
      isEmpty = false;
      TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
      // 獲取具體解析的TypeAdapter
      TypeAdapter<T> typeAdapter = getAdapter(typeToken);
      // 返回解析的結(jié)果
      T object = typeAdapter.read(reader);
      return object;
    } catch (EOFException e) {
    ...
    }
}

由于TypeAdapter的子類(lèi)實(shí)在是太多了,如果要去仔細(xì)的翻源碼太耗時(shí)間,直接斷點(diǎn):


在這里插入圖片描述

我們要找的就是ReflectiveTypeAdapterFactory的內(nèi)部類(lèi)Adapter,直接找他的read方法:

@Override public T read(JsonReader in) throws IOException {
      if (in.peek() == JsonToken.NULL) {
        in.nextNull();
        return null;
      }
        // 這里創(chuàng)建對(duì)象了
      T instance = constructor.construct();
        // 解析對(duì)應(yīng)的屬性,忽略
      try {
        in.beginObject();
        while (in.hasNext()) {
          String name = in.nextName();
          BoundField field = boundFields.get(name);
          if (field == null || !field.deserialized) {
            in.skipValue();
          } else {
            field.read(in, instance);
          }
        }
      } catch (IllegalStateException e) {
        throw new JsonSyntaxException(e);
      } catch (IllegalAccessException e) {
        throw new AssertionError(e);
      }
      in.endObject();
      return instance;
    }

終于找到創(chuàng)建對(duì)象的代碼,讓我們看看這個(gè)constructor到底是個(gè)什么玩意,經(jīng)過(guò)各種定位,最終我們找到了分析問(wèn)題的最關(guān)鍵類(lèi):ConstructorConstructor。

public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
    final Type type = typeToken.getType();
    final Class<? super T> rawType = typeToken.getRawType();
    // 優(yōu)先使用Type獲取對(duì)象的構(gòu)造方法
    final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);
    if (typeCreator != null) {
      return new ObjectConstructor<T>() {
        @Override public T construct() {
          return typeCreator.createInstance(type);
        }
      };
    }

    // 再次使用rawType獲取對(duì)象的構(gòu)造方法
    @SuppressWarnings("unchecked") // types must agree
    final InstanceCreator<T> rawTypeCreator =
        (InstanceCreator<T>) instanceCreators.get(rawType);
    if (rawTypeCreator != null) {
      return new ObjectConstructor<T>() {
        @Override public T construct() {
          return rawTypeCreator.createInstance(type);
        }
      };
    }
    
    /*** 分割線(xiàn)以上都是instanceCreators取出來(lái)的**/

    // 通過(guò)默認(rèn)的無(wú)參構(gòu)造方法,請(qǐng)注意是無(wú)參的構(gòu)造方法
    ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
    if (defaultConstructor != null) {
      return defaultConstructor;
    }

    // 以上三步仍沒(méi)有找到構(gòu)造方法時(shí)的處理
    ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
    if (defaultImplementation != null) {
      return defaultImplementation;
    }

    // 通過(guò)不安全的形式創(chuàng)建對(duì)象???
    return newUnsafeAllocator(type, rawType);
  }

其中前兩中方法都是我們通過(guò)配置Gson可以設(shè)置的,因?yàn)槲覀儾](méi)有設(shè)置,所以這里先跳過(guò),我們需要關(guān)注后三步,第一步:找到無(wú)參的構(gòu)造方法:

private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType) {
    try {
      // 反射無(wú)參的構(gòu)造方法
      final Constructor<? super T> constructor = rawType.getDeclaredConstructor();
      if (!constructor.isAccessible()) {
        accessor.makeAccessible(constructor);
      }
      return new ObjectConstructor<T>() {
        @Override public T construct() {
          try {
            Object[] args = null;
            // 調(diào)用無(wú)參構(gòu)造方法創(chuàng)建實(shí)例
            return (T) constructor.newInstance(args);
          } catch (Exception e) {
            ...
          }
          
        }
      };
      // 沒(méi)有無(wú)參構(gòu)造方法直接返回null
    } catch (NoSuchMethodException e) {
      return null;
    }
  }

熟悉反射的話(huà),這里都很好理解,如果還不太熟悉反射的話(huà),可以先去查看一下反射的知識(shí)。如果沒(méi)有無(wú)參的構(gòu)造方法,會(huì)進(jìn)入newDefaultImplementationConstructor:

private <T> ObjectConstructor<T> newDefaultImplementationConstructor(
      final Type type, Class<? super T> rawType) {
    if (Collection.class.isAssignableFrom(rawType)) {
        ... 集合子類(lèi)會(huì)有一些創(chuàng)建操作
    }

    if (Map.class.isAssignableFrom(rawType)) {
        ... Map子類(lèi)的創(chuàng)建操作
    }

    return null;
  }

newDefaultImplementationConstructor只對(duì)集合和Map的子類(lèi)有創(chuàng)建操作,很明顯TestBean只是一個(gè)普通對(duì)象,并不符合需求。經(jīng)過(guò)以上四步,我們沒(méi)有找到任何可以創(chuàng)建TestBean的方法,那么唯一的答案就只能是在newUnsafeAllocator(type, rawType)中了:

public static UnsafeAllocator create() {
    // 最關(guān)鍵
    try {
     // 反射找到sun.misc.Unsafe類(lèi)
      Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
      // 找到sun.misc.Unsafe類(lèi)中的theUnsafe屬性
      Field f = unsafeClass.getDeclaredField("theUnsafe");
      // 激活theUnsafe屬性
      f.setAccessible(true);
      // 得到theUnsafe的對(duì)象
      final Object unsafe = f.get(null);
      // 反射allocateInstance方法
      final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
      return new UnsafeAllocator() {
        @Override
        @SuppressWarnings("unchecked")
        public <T> T newInstance(Class<T> c) throws Exception {
          assertInstantiable(c);
          // 調(diào)用allocateInstance方法,創(chuàng)建類(lèi)型為c的對(duì)象
          return (T) allocateInstance.invoke(unsafe, c);
        }
      };
    } catch (Exception ignored) {
    }

    // 第二步的實(shí)現(xiàn)方案和第一步其實(shí)一樣,所以忽略
    ...

    // 第三步,在我的版本源碼中ObjectInputStream找不到newInstance方法,所以忽略
    ...     
  }

還是反射相關(guān)的調(diào)用,理解起來(lái)并不難,重點(diǎn)是理解sun.misc.Unsafe類(lèi)到底是個(gè)什么東西,可以無(wú)視對(duì)象的構(gòu)造方法,創(chuàng)建新的對(duì)象,我從網(wǎng)上分享的源碼中,截取一部分注釋的描述一下它:

sun.misc.Unsafesh收集了很多底層的不安全的操作方法。盡管類(lèi)和方法是開(kāi)放的,但是要謹(jǐn)慎的使用它,因?yàn)橹挥行湃蔚拇a才可以使用它。
allocateInstance方法:創(chuàng)建一個(gè)實(shí)例,但是不運(yùn)行它的構(gòu)造方法,請(qǐng)手動(dòng)初始化。

sun.misc.Unsafe還有很多其他的native方法,Google已經(jīng)明確說(shuō)明要謹(jǐn)慎使用,除非是極其特殊的情況,我們還是把它記在心里就好了。

總結(jié)

現(xiàn)在我們已經(jīng)了解了Gson創(chuàng)建對(duì)象的過(guò)程,那么一開(kāi)始的問(wèn)題要怎么解決呢?經(jīng)過(guò)分析源碼我們有以下兩種方案:

第一種方案::Gson配置TypeAdapter。

 val testBean2 = GsonBuilder().registerTypeAdapter(
            TestBean::class.java, TestBeanTypeAdapter()
        ).create().fromJson<TestBean>(jsonStr, TestBean::class.java)

class TestBeanTypeAdapter : JsonSerializer<TestBean?>,
    JsonDeserializer<TestBean?> {

    @Throws(JsonParseException::class)
    override fun deserialize(
        json: JsonElement?, typeOfT: Type?,
        context: JsonDeserializationContext?
    ): TestBean? {
        return if (json == null) {
            null
        } else {
            if (json is JsonObject) {
                return TestBean(json.get("type").asInt, json.get("name").asString)
            } else {
                return null
            }
        }
    }

    override fun serialize(
        src: TestBean?, typeOfSrc: Type?,
        context: JsonSerializationContext?
    ): JsonElement? {
        return JsonPrimitive(Gson().toJson(src))
    }
}

第二種方案:為T(mén)ypeAdapter,設(shè)置無(wú)參的構(gòu)造方法:

class TestBean {

    var type: Int = 0

    var name: String = ""

    var time: Long = 10

    init {
        Log.e("lzp", "TestBean create")
    }

    constructor(type: Int, name: String){
        this.type = type
        this.name = name
    }

    override fun toString(): String {
        return "type:$type, name:$name, time:$time"
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容