前言
最近做項(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"
}
}