Gson反序列化使用构建器生成的类

2 篇文章 0 订阅
2 篇文章 0 订阅

Student类



public class Student {
    private  final int id ;
    private  final int grade ;
    private  final int gender ;
    private  final String name ;

    public static class Builder{
        private final String name;
        private final int id;
        private  int grade;
        private  int gender;

        public Builder(String name,int id){
            this.name = name;
            this.id = id;
        }
        public Builder grade(int val){
            grade = val;
            return this;
        }
        public Builder gender(int val){
            gender = val;
            return this;
        }
        public Student build(){
            return new Student(this);
        }
    }

    private Student (Builder builder){
        id = builder.id;
        gender = builder.gender;
        grade = builder.grade;
        name = builder.name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", grade=" + grade +
                ", gender=" + gender +
                ", name='" + name + '\'' +
                '}';
    }
}

Teacher类:

public class Teacher {
    private  final int id ;
    private  final int grade ;
    private  final int gender ;
    private  final String name ;

    public static class Builder{
        private  String name;
        private  int id;
        private  int grade;
        private  int gender;

        public Builder name(String val){
            name = val;
            return this;
        }
        public Builder id(int val){
            id = val;
            return this;
        }
        public Builder grade(int val){
            grade = val;
            return this;
        }
        public Builder gender(int val){
            gender = val;
            return this;
        }
        public Teacher build(){
            return new Teacher(this);
        }
    }

    private Teacher (Builder builder){
        id = builder.id;
        gender = builder.gender;
        grade = builder.grade;
        name = builder.name;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "id=" + id +
                ", grade=" + grade +
                ", gender=" + gender +
                ", name='" + name + '\'' +
                '}';
    }
}

两个类的唯一区别就是,Teacher的静态内部类Builder没有自定义构造函数,拥有默认构造函数。

在后端Gson使用字符串反序列化可以正确生成对象

    public static void main(String[] args) throws IOException {
        Gson gson = new Gson();
        String jsonString = "{\"id\":123,\"name\":\"test\",\"grade\":100,\"gender\":1}";
        Student student = gson.fromJson(jsonString,Student.class);
        Teacher teacher = gson.fromJson(jsonString,Teacher.class);
        System.out.println("student:"+student);
        System.out.println("teacher:"+teacher);
    }
}

但是在controller层接口处直接接受Student类型则会报错:No primary or default constructor found for class Student 

而Teacher类则不会报错(HttpMessageConvert 已经更改为gson,如何更改可以看我的另一篇博客

@Controller
public class TestController {
    private static Gson gson = new Gson();
    @RequestMapping("/student")
    @ResponseBody
    public String stu(Student student){
        System.out.println(student);
        return student.toString();
    }

    @RequestMapping("/teacher")
    @ResponseBody
    public Teacher teacher(Teacher teacher){
        return teacher;
    }
}

这是为什么呢?我们先看一下gson是怎样创建对象的。

InstanceCreator和ObjectConstructor

经过断点调试或者阅读源代码不难发现,GSON是使用ObjectConstructor来创建对象实例的,这点从代码注释里也能看的出来:

/**
 * 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里:

  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);
  }

代码看起来很复杂,但实际上井然有序:

    1、如果我们(通过GsonBuilder)注册过InstanceCreator,则交给InstanceCreator来创建实例
    2、如果类有默认构造函数,则通过反射调用默认构造函数创建实例
    3、如果想要创建List或Map等接口的实例,则走这里
    4、否则交给神秘的UnsafeAllocator来收场

UnsafeAllocator

现在让我们一睹UnsafeAllocator的风采:

/**
 * 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);
      }
    };
  }
}

去掉反射后,代码看起来大概是这样:

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); // <--
        }  
      }; 
  }
}

为什么在后端直接使用字符串可以反序列化出Student而在Controller层不可以直接接受Student参数呢。

我认为是在HttpMessageConvert中不使用UnsafeAllocator。所以必须要注册InstanceCreator或者有默认构造函数。

 

接下来我们注册一个Student的InstanceCreator


public class StudentInstanceCreator implements InstanceCreator<Student> {
    private Student.Builder builder;
    public StudentInstanceCreator(Student.Builder builder){
        this.builder = builder;
    }
    @Override
    public Student createInstance(Type type) {
        Student student = builder.build();
        return student;
    }
}

然后在HttpMessageConvert中注册

@Configuration
@ComponentScan("Controller")
@EnableWebMvc
public class WebApp implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(createGsonHttpMessageConverter());
        WebMvcConfigurer.super.configureMessageConverters(converters);
    }

    private GsonHttpMessageConverter createGsonHttpMessageConverter() {
        Gson gson = new GsonBuilder()
                .setDateFormat("yyyy-MM-dd HH:mm:ss")
                .registerTypeAdapter(Student.class,new StudentInstanceCreator(new Student.Builder(null,0)))
                .create();

        GsonHttpMessageConverter gsonConverter = new GsonHttpMessageConverter();
        gsonConverter.setGson(gson);

        return gsonConverter;
    }
}

就是GsonBuilder().registerTypeAdapter(Student.class,new StudentInstanceCreator(new Student.Builder(null,0)))
                .create();这一句就ok类。

controller可以直接接受Student类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值