Gson反序列化原理
原理簡述
gson反序列化主要分為兩個過程:
- 根據TypeToken建立出物件
- 根據json字串解析資料,對物件屬性賦值
物件的建立
ConstructorConstructor.get
- 先嚐試獲取無參建構函式
- 失敗則嘗試List、Map等情況的建構函式
- 最後使用Unsafe.newInstance兜底(此兜底不會呼叫建構函式,導致所有物件初始化程式碼不會呼叫)
public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
final Type type = typeToken.getType();
final Class<? super T> rawType = typeToken.getRawType();
// first try an instance creator
@SuppressWarnings("unchecked") // types must agree
final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);
if (typeCreator != null) {
return new ObjectConstructor<T>() {
@Override public T construct() {
return typeCreator.createInstance(type);
}
};
}
// Next try raw type match for instance creators
@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);
}
};
}
// 獲取無參建構函式
ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
if (defaultConstructor != null) {
return defaultConstructor;
}
// 獲取List<T>,Map<T>等建構函式,對於List,Map的情況
ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
if (defaultImplementation != null) {
return defaultImplementation;
}
// unSafe構造出物件,不呼叫任何的建構函式
// finally try unsafe
return newUnsafeAllocator(type, rawType);
}
複製程式碼
ConstructorConstructor.newDefaultConstructor
- 呼叫Class.getDeclaredConstructor獲取無參建構函式
private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType) {
try {
// 獲取無參建構函式
final Constructor<? super T> constructor = rawType.getDeclaredConstructor();
if (!constructor.isAccessible()) {
accessor.makeAccessible(constructor);
}
複製程式碼
ConstructorConstructor.newUnsafeAllocator
- 呼叫UnSafe.newInstance建立出物件
- 不會呼叫建構函式,因此所有的初始化的程式碼都不會被呼叫
private <T> ObjectConstructor<T> newUnsafeAllocator(
final Type type, final Class<? super T> rawType) {
return new ObjectConstructor<T>() {
private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
@SuppressWarnings("unchecked")
@Override public T construct() {
try {
//
Object newInstance = unsafeAllocator.newInstance(rawType);
return (T) newInstance;
} catch (Exception e) {
throw new RuntimeException(("Unable to invoke no-args constructor for " + type + ". "
+ "Registering an InstanceCreator with Gson for this type may fix this problem."), e);
}
}
};
}
複製程式碼
結論
- Gson反序列要工作正常,使結果符合預期的話,要求類必須有一個無參建構函式
kotlin建構函式預設引數和無參建構函式的關係
引數裡面存在沒有預設值的情況
kotlin程式碼
- id沒有預設值
class User(val id: Int, val name: String = "sss") {
init {
println("init")
}
}
複製程式碼
反編譯的Java程式碼
- 包含兩個建構函式,一個是我們宣告的全引數建構函式,另一個是kotlin生成的輔助建構函式
- 不包含無參建構函式
public final class User {
private final int id;
@NotNull
private final String name;
public User(int id, @NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.id = id;
this.name = name;
String var3 = "init";
System.out.println(var3);
}
// $FF: synthetic method
public User(int var1, String var2, int var3, DefaultConstructorMarker var4) {
if ((var3 & 2) != 0) {
var2 = "";
}
this(var1, var2);
}
}
複製程式碼
gson反序列化輸出
程式碼:
@Test
fun testJson() {
val user = Gson().fromJson("{}", User::class.java)
print(user.name)
}
複製程式碼
輸出:不符合預期(我們宣告的非空的name實際結果是null)
null
Process finished with exit code 0
複製程式碼
引數都包含預設引數的情況
kotlin程式碼
class User(val id: Int=1, val name: String = "sss") {
init {
println("init")
}
}
複製程式碼
反編譯Java程式碼
- 除了上面的兩個建構函式,多了一個無參建構函式(從邏輯上講,這個也符合預期)
public final class User {
private final int id;
@NotNull
private final String name;
public User(int id, @NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.id = id;
this.name = name;
String var3 = "init";
System.out.println(var3);
}
// $FF: synthetic method
public User(int var1, String var2, int var3, DefaultConstructorMarker var4) {
if ((var3 & 1) != 0) {
var1 = 1;
}
if ((var3 & 2) != 0) {
var2 = "";
}
this(var1, var2);
}
// 無參建構函式
public User() {
this(0, (String)null, 3, (DefaultConstructorMarker)null);
}
}
複製程式碼
gson反序列化輸出
程式碼:
@Test
fun testJson() {
val user = Gson().fromJson("{}", User::class.java)
print(user.name)
}
複製程式碼
輸出:符合預期
init
sss
Process finished with exit code 0
複製程式碼
Best Practice
Practice1
- 屬性宣告在建構函式,所有引數都帶預設值
- 不確定的引數宣告為可空
class User(val id: Int=1 , val name: String = "sss") {
init {
println("init")
}
}
複製程式碼
Practice2
- 迴歸到Java的寫法即可
class User {
val id: Int = 1
val name: String = "sss"
init {
println("init")
}
}
複製程式碼