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类