1. 背景
昨天在写RPC的基础Demo的时候,使用JSON作为序列化方式,然后在序列化对象的时候,报错了。
 我复现一下该报错:
public class GsonTest {
    public static void main(String[] args) {
        new Gson().toJson(String.class);
     }
 }
具体错误如下:
Exception in thread "main" java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: java.lang.String. Forgot to register a type adapter?
	at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:73)
	at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:69)
	at com.google.gson.TypeAdapter$1.write(TypeAdapter.java:191)
	at com.google.gson.Gson.toJson(Gson.java:704)
	at com.google.gson.Gson.toJson(Gson.java:683)
	at com.google.gson.Gson.toJson(Gson.java:638)
	at com.google.gson.Gson.toJson(Gson.java:618)
	at chat.rpc.GsonTest.main(GsonTest.java:13)
2. 解决方法:
main {
	Gson gson = new GsonBuilder()
                .registerTypeAdapter(Class.class, new ClassTypeAdapter()).
                create();
}
  // 方式一:继承 TypeAdapter
 class ClassTypeAdapter extends TypeAdapter<Class> {
        @Override
        public void write(JsonWriter out, Class value) throws IOException {
            out.value(value.getName());
        }
        @Override
        public Class read(JsonReader in) throws IOException {
            String clazzName = in.nextString();
            try {
                return Class.forName(clazzName);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
    }
// 方式二:实现两个接口,JsonSerializer和JsonDeserialize。
class ClassTypeAdapter2 implements JsonSerializer<Class>, JsonDeserializer<Class> {
        @Override
        public Class deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            String asString = json.getAsString();
            try {
                return Class.forName(asString);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        @Override
        public JsonElement serialize(Class src, Type typeOfSrc, JsonSerializationContext context) {
            return new JsonPrimitive(src.getName());
        }
    }
3. 源码分析
-  老规矩,报错了,在日志中,点击TypeAdapters.java:73,下载源码,查看具体源代码  
 打印的日志是:试图去序列化这个Class,但是没有注册类型适配器。
 啥是类型适配器?TypeAdapter? 什么是TypeAdapter啊,重写的write和read是干嘛用的,看一下这个类吧
-  查看这个TypeAdapter抽象类,注释写明是进行JSON和对象的转换的。write用于把对象转为JSON, read用于读取JSON并且转为对象。还说可以自定义实现,只要实现TypeAdapter接口就行了。不管了,先回过头看一下这个CLASS在哪里调用,看一下这个对象的引用吧 
  
  
-  点击实现: 
  
 哦豁,是在创建TypeAdapterFactory工厂的时候创建的,看源码,还是老套路,Idea左侧的Structure打开(快速了解类的内部结构),卧槽了。好多的TypeAdapter和对应的TypeAdapterFactory。
  
-  回想一下,Gson本身是序列化的,那么就应该包含各种类型适配器,这没问题,对Java的各种类型进行序列化和反序列化。随便点开String的TypeAdapter看看。read和write都有实现啊,没有报错,那咋Class的TypeAdapter就直接在代码里面抛异常了嘞?(看第一个截图) 
  
-  那我是不是得重新写一遍这个ClassTypeAdapter啊,把read和write实现一遍?这里有这么多的TypeAdapter,全给对应的TypeAdapterFactory拿去使用了,我需要在哪里接入这个ClassTypeAdapter呢?那我看看这个CLASS_FACTORY在哪里被调用。 
  
-  哎玛,在Gson这个类这添加的,全部类型都在这加,并且最后Gson的对象this.factories指向了这个factories的集合。 
  
-  看一下这些代码的位置,用来是构造器里面的代码。 
  
 既然是在构造器的时候构造的,那么Gson肯定是考虑到了拓展问题,看一下,参数里最后有3个TypeAdapterFactory的列表。第三个的名字可以啊,要添加的factories!, 有线索。
 用户的类型适配器,是从构造器传进来的,再挖一下,构造器是在哪里使用。
  
-  继续看Gson构造器的调用方,看看能在哪里塞个自定义的TypeAdapter或者对应的工厂类进去。 
  
 只有两个地方调用了这个Gson。第一个Gson()的构造方法调用到的,但是这后面的三个参数都是空列表呀,还有一个是Gsonbuilder,builder一般用于构建对象并且加载组件用。看看这个GsonBuilder.create()
-  确实,Gsonbuilder.create最后会调用new Gson() 返回一个Gson对象,里面factories是由588行,this.factories传入。 
  
-  那接下来看看这个factories是在哪里被赋值的 
  
 好东西啊,有registerTypeAdapter和registerTypeAdapterFactory这两个方法add。还是两个public方法。
 赶紧瞅一瞅。registerTypeAdaper。看注释第一句,配置自定义序列化和反序列化的。
 下方的registerTypeAdaperFactory还得去构造个一个工厂,有点麻烦啊,先看看registerTypeAdaper方法能不能解决问题。
  
-  注释写的很清楚啊,配置Gson for 自定义序列化和反序列化,但是如果一个类型适配器在这之前被这种方式注册,那么就会被覆盖。那我那个ClassTypeAdaper不就可以在这重新自定义了吗!!!!而且好像有两种方式哦,第一种是实现JsonSerializer和JsonDeserializer。第二种是继承TypeAdaper。 
  
-  那我先写一个咯。 
  
 还是报错。
 好像有点问题,上面已经说了,如果一个类型适配器在这之前被这种方式注册,那么就会被覆盖。哦豁,那么我应该先注册自定义的factories,再注册Gson提供的factories。前面是有个builder.create的,里面是先加入这个factories,然后再new Gson()的。
  
 并且这new Gson中,上来就把builder里面的自定义的factories先加入Gson的factories的
  
-  那我再试试。先别new Gson. 用builder试试。哦豁好起来了。 
  
总结:
以上是代码分析流程。整体的逻辑deug过,这里不展开去梳理gson的代码逻辑了。我好奇为什么Class的TypeAdapter没有实现,而是抛异常,这是chatgpt给出的答案。
 
一般而已,哪里抛异常了,就在那里打个断点,然后debug可以看到完整的堆栈信息,记得先把源码下载下来,同时类的结构在idea中记得展开,多阅读注释信息。一般构造器都会提供口子给自定义组件支持自定义开发,如果没有,可以通过多态(会破坏设计原则)或者反射等方式去实现。比如spring-kafka.这两天写一篇spring-kafka兜底方案是如何实现的(重试如何告警,死信队列Topic自定义,推送告警,死信消息推送失败告警等)






![基于gin-vue-admin[gin+gorm]手动实现crud(全)](https://img-blog.csdnimg.cn/8c43b1cc45084e648a4e85e060c58402.png)












![[MySQL索引]2.索引的底层原理(一)](https://img-blog.csdnimg.cn/76eb7e5dda794e509392c88a92a130ba.png)