使用Gson-extras实现带transient变量的多态JSON反序列化问题
我完全懂你遇到的这个棘手问题:当一个类同时具备多态特性和transient变量,并且作为其他类的属性进行序列化时,只要子类带有@PostConstruct注解,RuntimeTypeAdapterFactory负责生成的type字段就会凭空消失,直接导致多态反序列化失效。这本质上是两个AdapterFactory的逻辑冲突——PostConstructAdapterFactory在处理带有@PostConstruct的类时,完全跳过了RuntimeTypeAdapterFactory的多态处理逻辑,自己接管了序列化流程。
问题根源拆解
PostConstructAdapterFactory的默认实现会为带有@PostConstruct注解的类直接生成一个自定义TypeAdapter,不会复用RuntimeTypeAdapterFactory已经注册好的多态TypeAdapter。当子类以父类类型的身份作为其他类的属性被序列化时,PostConstructAdapterFactory的TypeAdapter会被优先选中,自然就跳过了RuntimeTypeAdapterFactory添加type字段的关键步骤。
解决方案:自定义融合型AdapterFactory
我们需要编写一个自定义的AdapterFactory,把RuntimeTypeAdapterFactory的多态处理逻辑和PostConstructAdapterFactory的初始化逻辑深度结合,确保两种功能同时生效。核心思路是:先让RuntimeTypeAdapterFactory生成处理多态的TypeAdapter,再用PostConstruct的逻辑包装这个Adapter,既保证序列化时保留type字段,又能在反序列化后执行@PostConstruct方法初始化transient变量。
步骤1:实现自定义CombinedTypeAdapterFactory
import com.google.gson.Gson; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import org.springframework.beans.factory.annotation.PostConstruct; import java.io.IOException; import java.lang.reflect.Method; public class CombinedTypeAdapterFactory implements TypeAdapterFactory { private final RuntimeTypeAdapterFactory<?> runtimeFactory; public CombinedTypeAdapterFactory(RuntimeTypeAdapterFactory<?> runtimeFactory) { this.runtimeFactory = runtimeFactory; } @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { // 先获取RuntimeTypeAdapterFactory生成的多态TypeAdapter TypeAdapter<T> runtimeAdapter = runtimeFactory.create(gson, type); if (runtimeAdapter == null) { return null; } // 检查当前类型是否有@PostConstruct方法(递归检查父类) Method postConstructMethod = findPostConstructMethod(type.getRawType()); if (postConstructMethod == null) { return runtimeAdapter; } // 包装RuntimeTypeAdapter,添加PostConstruct逻辑 return new TypeAdapter<T>() { @Override public void write(JsonWriter out, T value) throws IOException { // 复用RuntimeTypeAdapter的序列化逻辑,保证type字段被写入 runtimeAdapter.write(out, value); } @Override public T read(JsonReader in) throws IOException { // 先用RuntimeTypeAdapter反序列化出对象 T instance = runtimeAdapter.read(in); // 执行@PostConstruct方法初始化transient变量 try { postConstructMethod.setAccessible(true); postConstructMethod.invoke(instance); } catch (Exception e) { throw new RuntimeException("Failed to invoke @PostConstruct method", e); } return instance; } }; } private Method findPostConstructMethod(Class<?> clazz) { for (Method method : clazz.getDeclaredMethods()) { if (method.isAnnotationPresent(PostConstruct.class)) { return method; } } // 递归检查父类,支持继承场景 Class<?> superClass = clazz.getSuperclass(); if (superClass != null && superClass != Object.class) { return findPostConstructMethod(superClass); } return null; } }
步骤2:修改GsonUtils的配置
把原来分别注册两个Factory的方式,改为注册我们自定义的CombinedTypeAdapterFactory,同时传入RuntimeTypeAdapterFactory实例:
public class GsonUtils { private static final RuntimeTypeAdapterFactory<BaseClass> BASE_CLASS_RUNTIME_FACTORY = RuntimeTypeAdapterFactory.of(BaseClass.class); private static final GsonBuilder gsonBuilder = new GsonBuilder() .registerTypeAdapterFactory(new CombinedTypeAdapterFactory(BASE_CLASS_RUNTIME_FACTORY)); // 提供注册子类的公共方法 public static void registerBaseClassSubtype(Class<? extends BaseClass> subtype) { BASE_CLASS_RUNTIME_FACTORY.registerSubtype(subtype); } public static Gson getGson() { return gsonBuilder.create(); } }
步骤3:调整BaseClass的注册逻辑
原来的BaseClass构造函数中注册子类的方式可以简化,直接调用GsonUtils的方法即可:
public class BaseClass { public BaseClass() { // 注册当前子类到RuntimeTypeAdapterFactory GsonUtils.registerBaseClassSubtype(this.getClass()); } }
步骤4:验证WrapperClass场景
现在测试WrapperClass的序列化和反序列化,确认type字段存在且transient变量被正确初始化:
public class WrapperClass { protected BaseClass dc = new DerivedClass(6); } public static void main(String[] args) { Gson gson = GsonUtils.getGson(); String serialized = gson.toJson(new WrapperClass()); System.out.println("Serialized JSON: " + serialized); // 输出应包含"type":"DerivedClass"字段 WrapperClass wc = gson.fromJson(serialized, WrapperClass.class); DerivedClass dc = (DerivedClass) wc.dc; System.out.println("Doubled value: " + dc.doubled); // 预期输出12 }
关键说明
- 自定义Factory优先复用RuntimeTypeAdapterFactory的逻辑,从根源保证多态序列化时
type字段不会丢失。 - 仅在类存在
@PostConstruct方法时才添加初始化逻辑,兼容没有该注解的多态类。 - 递归查找父类的
@PostConstruct方法,确保继承场景下的逻辑正常生效。
内容的提问来源于stack exchange,提问作者johndoe

