GSON源码分析之适配器模式及自定义适配器Adapter

GSON源码分析之适配器模式及自定义适配器Adapter

​ FastJson最近频频爆出严重漏洞,所以决定长痛不如短痛,把已有项目的FastJson全部替换成GSON。因为要尽量要避免改动业务代码,所以必须对GSON做深入研究,与FastJson比对并解决序列化与反序列化差异问题。因为差异较多,此文主要分析一下适配器源码,及其自定义适配器Adapter。

源码分析

1:入口GSON.fromJson(JsonReader reader, Type typeOfT):

在这里插入图片描述

2:分析获取getAdapter方法:

/**
 * Returns the type adapter for {@code} type.
 * 此方法获取目标类型全部字段所对应的适配器,对象内部都是使用递归的方式获取
 * @throws IllegalArgumentException if this GSON cannot serialize and
 *     deserialize {@code type}.
 */
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
  // 尝试从GSON缓存中获取Adapter
  TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
  if (cached != null) {
    return (TypeAdapter<T>) cached;
  }

  //从ThreadLocal获取FutureTypeAdapter
  //FutureTypeAdapter是TypeAdapter的一个代理模式的包装类,可以看作就是TypeAdapter
  Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
  //是否需要清理ThreadLocal
  boolean requiresThreadLocalCleanup = false;
    
  //首次进入此方法创建Map,并标记使用完成清理ThreadLocal
  if (threadCalls == null) {
    threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
    calls.set(threadCalls);
    requiresThreadLocalCleanup = true;
  }

  // the key and value type parameters always agree
  //尝试从ThreadLocal中获取已创建好的Adapter,此处主要为递归时准备,可以避免栈溢出问题
  FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
  if (ongoingCall != null) {
    return ongoingCall;
  }

  try {
    //创建FutureTypeAdapter
    FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
    //加入ThreadLocal
    threadCalls.put(type, call);

    //遍历GSON已注册的所有Adapter
    //我们自定义的Adapter就是注册到factories的
    for (TypeAdapterFactory factory : factories) {
      //尝试匹配每个适配器,此方法内部递归调用getAdapter()
      //
      TypeAdapter<T> candidate = factory.create(this, type);
      if (candidate != null) {
        //匹配成功的适配器赋值到ThreadLocal
        call.setDelegate(candidate);
        //加入缓存
        typeTokenCache.put(type, candidate);
        return candidate;
      }
    }
    throw new IllegalArgumentException("GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type);
  } finally {
    //每次从ThreadLocal移除已匹配到的适配器
    threadCalls.remove(type);

    //当已经创建完所有适配器的时候,释放ThreadLocal
    if (requiresThreadLocalCleanup) {
      calls.remove();
    }
  }
}

3:getAdapter的递归调用链:

getAdapter(TypeToken<T> type) ->
ReflectiveTypeAdapterFactory.create(Gson gson, final TypeToken<T> type) ->
ReflectiveTypeAdapterFactory.getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) ->
ReflectiveTypeAdapterFactory.createBoundField( final Gson context, final Field field, final String name,final TypeToken<?> fieldType, boolean serialize, boolean deserialize) ->
getAdapter(TypeToken<T> type)

4:分析

​ 代码考虑到多线程问题,所以使用ThreadLocal在主线程中进行缓存,单次结束后移出ThreadLocal,这也节约了其他线程获取同类型适配器创建适配器所需消耗的资源。

​ 还有一个设计巧妙的地方,也就是使用包装类的意义:

在这里插入图片描述

自定义Adapter:

1:继承抽象类TypeAdapter,实现read()和write()方法,例如时间适配器。

package com.tencent.trip.util.gson;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
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.JsonToken;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.Locale;

/**
 * @Description: 时间适配器
 * @Author: 
 * @Date: 10:18 2020/4/27
 */
public class CustDateTypeAdapter extends TypeAdapter<Date> {

    public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {

        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {

            @SuppressWarnings("unchecked")
            TypeAdapter<T> typeAdapter = (TypeAdapter<T>) ((typeToken.getRawType() == Date.class) ? new CustDateTypeAdapter()
                    : null);
            return typeAdapter;
        }
    };
    private final DateFormat zhCNFormat;
    private final DateFormat dateFormat;

    public CustDateTypeAdapter() {
        this.zhCNFormat = DateFormat.getDateTimeInstance(2, 2, Locale.SIMPLIFIED_CHINESE);
        this.dateFormat = DateFormat.getDateInstance(2, Locale.SIMPLIFIED_CHINESE);
    }


    @Override
    public Date read(JsonReader in) throws IOException {
        if (in.peek() == JsonToken.NULL) {
            in.nextNull();
            return null;
        }
        return deserializeToDate(in.nextString());
    }

    private synchronized Date deserializeToDate(String json) {
        try {
            return new Date(Long.parseLong(json));
        } catch (Exception e) {
            try {
                return this.zhCNFormat.parse(json);
            } catch (ParseException e1) {
                try {
                    return this.dateFormat.parse(json);
                } catch (ParseException e2) {
                    throw new JsonSyntaxException(json, e2);
                }
            }
        }
    }

    @Override
    public synchronized void write(JsonWriter out, Date value)
            throws IOException {
        if (value == null) {
            out.nullValue();
            return;
        }
        String dateFormatAsString = this.zhCNFormat.format(value);
        out.value(dateFormatAsString);
    }
}

2:注册到GSON:

        //注册适配器日期类型
      Gson GSON = new GsonBuilder().registerTypeAdapter(Date.class, new CustDateTypeAdapter())
      .create();
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值