new Gson()和GsonBuilder().registerTypeAdapter(...).create()得到的都是 Gson 对象,都可以调用 toJson ()。 区别不是 “有参 / 无参构造 Gson”,区别是:这个 Gson 实例有没有装上自定义的类型转换器ClassCodec。
//版本A:裸Gson,没有注册Class类型适配器 Gson gson1 = new Gson(); //版本B:GsonBuilder构建,注册了Class类型的自定义编解码器 Gson gson2 = new GsonBuilder() .registerTypeAdapter(Class.class, new Serializer.ClassCodec()) .create();1. 普通对象,两者完全一样
如果你的对象里没有Class<?>类型的字段,比如普通 DTO:
class User{ Long id; String name; }gson1.toJson(user)和gson2.toJson(user)输出的 JSON 字符串一模一样。 这个时候你完全可以用简单的new Gson()。
2.RPC 调用消息对象(Invocation)就不一样了!
RPC 的调用报文对象,一定会携带Class类型成员:
class RpcInvocation{ private String methodName; private Class<?> interfaceClass; //❗这里是Class类型! private Class<?>[] paramTypes; //❗Class数组 private Object[] args; }👉原生 Gson(new Gson ())处理Class字段直接翻车Gson 原生不知道怎么序列化UserApi.class这种Class对象。 直接new Gson().toJson(invocation),会把 Class 对象内部一堆 JVM 元属性全部序列化出来,生成一大坨垃圾 JSON,反序列化根本还原不回来。
所以手写了ClassCodec适配器:
- 序列化时:把
Class<?>对象,转为它的全类名字符串"com.xxx.UserApi" - 反序列化时:拿到字符串,
Class.forName(全类名),重新加载出 Class 对象
registerTypeAdapter(Class.class, new ClassCodec())就是告诉这个 Gson 实例:只要遇到 Class 类型,不要用原生逻辑,用我写的这套编解码。
⚠️重点:这个适配器只属于 gson2 这个实例。
new Gson()出来的 gson1 完全不知道这个自定义逻辑,处理 Class 字段直接出错。
3. 不是 toJson 需要 “特殊入参”,是 Gson 实例需要提前装配适配器
toJson(object)方法本身没有变,传入的对象也没变。是 Gson 这个工具实例,内部预装了处理 Class 类型的规则。
流程:
- 用 GsonBuilder,注册
Class类型的自定义 Codec .create()生成一个带该规则的 Gson 对象- 调用
gson.toJson(对象),当遍历对象字段,碰到字段类型是Class,自动执行我们写好的 ClassCodec 序列化逻辑,输出全类名字符串。
4. 反序列化那边也要同一个 Gson!
//反序列化也必须用注册过Codec的gson对象 return gson.fromJson(json,clazz);接收方 fromJson 的时候,JSON 里面存的是全类名字符串,Gson 看到目标字段是 Class 类型,就会走 Codec 的反序列化逻辑,调用 Class.forName 还原 Class 对象。
如果接收方用裸
new Gson(),拿到字符串,不知道要转 Class,直接得到字符串,类型不匹配直接报错。
一句话总结
- 普通 POJO:
new Gson()完全够用。 - RPC 报文里面包含
Class/Class[]字段,必须用 GsonBuilder 注册自定义适配器,生成装配好规则的 Gson 实例。 - 不是 toJson 调用的时候传参数,是构建 Gson 工具对象的时候,提前装好特殊类型的处理逻辑。
踩坑提醒
很多新手会犯:序列化用带 Codec 的 Gson,反序列化写new Gson(),两边 Gson 实例不一样,反序列化直接报错。序列化、反序列化必须用同一个注册过适配器的 Gson 实例。
优化小提示
不要每次 serialize/deserialize 方法内部都 new 一遍 GsonBuilder,Gson 实例是线程安全的,可以提为静态常量,只构建一次。
//只初始化一次 private static final Gson GSON = new GsonBuilder() .registerTypeAdapter(Class.class, new Serializer.ClassCodec()) .create();