GSON使用笔记(2) -- 反序列化时GSON如何创建对象实例
2016-09-16 17:14
609 查看
从一个问题开始
假设有这么一个类:[java] view
plain copy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
class MyObj {
public final int x;
public MyObj(int x) {
this.x = x;
}
}
和下面的测试代码:
[java] view
plain copy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
@Test
public void gson() {
MyObj obj = new Gson().fromJson("{\"x\":1}", MyObj.class);
Assert.assertEquals(1, obj.x);
}
那么GSON是通过什么样的方式创建MyObj对象的呢?答案可能会出乎你的意料(至少出乎了我的意料)。
InstanceCreator和ObjectConstructor
经过断点调试或者阅读源代码不难发现,GSON是使用ObjectConstructor来创建对象实例的,这点从代码注释里也能看的出来:[java] view
plain copy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
/**
* Defines a generic object construction factory. The purpose of this class
* is to construct a default instance of a class that can be used for object
* navigation while deserialization from its JSON representation.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public interface ObjectConstructor<T> {
/**
* Returns a new instance.
*/
public T construct();
}
那么ObjectConstructor从何而来呢?答案在ConstructorConstructor里:
[java] view
plain copy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
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>() {
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>() {
public T construct() {
return rawTypeCreator.createInstance(type);
}
};
}
ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
if (defaultConstructor != null) {
return defaultConstructor;
}
ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
if (defaultImplementation != null) {
return defaultImplementation;
}
// finally try unsafe
return newUnsafeAllocator(type, rawType);
}
代码看起来很复杂,但实际上井然有序:
如果我们(通过GsonBuilder)注册过InstanceCreator,则交给InstanceCreator来创建实例
如果类有默认构造函数,则通过反射调用默认构造函数创建实例
如果想要创建List或Map等接口的实例,则走这里
否则交给神秘的UnsafeAllocator来收场
第一和第三种情况暂不考虑,下面来分析第二和第四种情况。
有默认构造函数的情况
按照前面的分析,这种情况GSON是通过调用默认构造函数来创建对象实例的,让我们证明这一点:[java] view
plain copy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
class MyObj {
public final int x;
public MyObj() {
throw new RuntimeException("!!!"); // <---
}
}
[java] view
plain copy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
@Test(expected = RuntimeException.class) // <---
public void gson() {
new Gson().fromJson("{\"x\":1}", MyObj.class);
}
测试通过!
没有默认构造函数的情况
还是通过代码来证明这一点:[java] view
plain copy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
class MyObj {
public final int x;
public MyObj(int x) { // <---
throw new RuntimeException("!!!");
}
}
[java] view
plain copy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
@Test
public void gson() {
MyObj obj = new Gson().fromJson("{\"x\":1}", MyObj.class);
Assert.assertEquals(1, obj.x);
}
测试通过!
UnsafeAllocator
现在让我们一睹UnsafeAllocator的风采:[java] view
plain copy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
/**
* Do sneaky things to allocate objects without invoking their constructors.
*
* @author Joel Leitch
* @author Jesse Wilson
*/
public abstract class UnsafeAllocator {
public abstract <T> T newInstance(Class<T> c) throws Exception;
public static UnsafeAllocator create() {
// try JVM
// public class Unsafe {
// public Object allocateInstance(Class<?> type);
// }
try {
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
Field f = unsafeClass.getDeclaredField("theUnsafe");
f.setAccessible(true);
final Object unsafe = f.get(null);
final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
return new UnsafeAllocator() {
@Override
@SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> c) throws Exception {
return (T) allocateInstance.invoke(unsafe, c);
}
};
} catch (Exception ignored) {
}
...
// give up
return new UnsafeAllocator() {
@Override
public <T> T newInstance(Class<T> c) {
throw new UnsupportedOperationException("Cannot allocate " + c);
}
};
}
}
去掉反射后,代码看起来大概是这样:
[java] view
plain copy
public abstract class UnsafeAllocator {
public abstract <T> T newInstance(Class<T> c) throws Exception;
public static UnsafeAllocator create() {
return new UnsafeAllocator() {
@Override
@SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> c) throws Exception {
Unsafe unsafe = sun.misc.Unsafe.theUnsafe; // <--
return (T) unsafe.allocateInstance(c); // <--
}
};
}
}
那么final字段是怎么处理的?
答案是,通过反射。详细情况可以参考这个问题,下面我们仅通过代码来证明这一点:[java] view
plain copy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
class MyObj {
public final int x;
public MyObj(int x) { // <---
this.x = x;
}
}
[java] view
plain copy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
@Test
public void setFinal() throws Exception {
MyObj obj = new MyObj(1);
Assert.assertEquals(1, obj.x);
Field f = obj.getClass().getField("x");
f.setAccessible(true); // <---
f.set(obj, 2);
Assert.assertEquals(2, obj.x);
}
测试通过!
结论
反序列化时,如果一个类没有默认构造函数,那么GSON是通过JDK内部API来创建对象实例的,并且通过反射给final字段赋值。这种做法通常是很危险的,所以非专业人士请勿效仿!
相关文章推荐
- GSON使用笔记(2) -- 反序列化时GSON如何创建对象实例
- ABAP--如何创建和使用sap的号码范围对象
- ABAP--如何创建和使用sap的号码范围对象
- 抽象工厂模式:简单工厂模式、工厂方法模式对比;在工厂方法模式中使用反射创建对象实例
- java文件对象的创建和使用(本文章只是初学者的笔记)
- ABAP--如何创建和使用sap的号码范围对象
- 如何让CloudStack使用KVM创建Windows实例成功识别并挂载数据盘
- 在spring来管理实例对象prototype和singleton的选择.针对action如何使用
- WPF学习笔记 - 如何用WPF创建单实例应用程序
- 创建智能网络蜘蛛——如何使用Java网络对象和HTML对象(翻译)
- ABAP--如何创建和使用sap的号码范围对象
- 设计模式学习笔记:就一句话的创建实例是如何演变成工厂模式的?
- [Erlang 学习笔记] 使用 rebar 创建 application(basho- lager 应用实例)
- 如何用创建命名对象来判断应用程序是否已有一个实例在运行?
- ADO.NET学习笔记——如何手动创建类型化DataSet对象
- 如何使用Gson序列化和反序列化
- 如何解决ASP使用FFMPEG进行视频截图时报出的“无法创建对象”问题
- 如何:创建和使用shared_ptr实例
- Java中如何实现一个类在内存里只能创建一个实例对象
- 笔记2-----创建一个java实例对象的方法