需求
在一个成绩管理系统中,有实体类Score和实体类Student,现需要对这两个实体类关联的数据库表分别进行批量插入,因而需要处理两种不同的JSON数据(均为数组形式),并转换为相应的List。在两种实体类http请求中,Student类中的成员变量与对应json数据格式的一致,而Score类中的成员变量则与相应http请求中的json数据格式则不一致,因此需要在转型的过程中进行自定义的操作。
分析
对于Student类,可以直接用springmvc提供的MappingJackson2HttpMessageConverter转换器进行类与json字符串之间的转换,对于Score类,则需要自定义转换器。自定义转换器我能想到的有两种方式:
一丶实现HttpMessageConverter接口
二丶继承MappingJackson2HttpMessageConverter类并对其功能进行扩展
在现在这种情况下,方式一实际上是行不通的,因为springmvc在选择转换器时,是通过转换器类中的canRead方法来进行判断的,放上两种方式中实际调用的canRead方法的签名:
实现HttpMessageConverter接口时:
boolean canRead(Class< ? > aClass, MediaType mediaType)
继承MappingJackson2HttpMessageConverter类时:
boolean canRead(Type type, Class< ? > aClass, MediaType mediaType)
可以看到两个方法的参数并不一样,对于第一种方式,转型的目的java对象类型信息(此处为List< Score >)存放于Class< ? > aClass中,该变量只能记录List擦除后的信息,无法记录List中持有对象的类型(只知道是List而不知道是持有什么类型的List),因而无法为springmvc对转换器的选择提供准确的判断信息;而对于第二种方式,Type type中存放了所有相关的详细信息(包括List的泛型信息),因而可行。
实验
接下来向
发送请求体为json格式的数据请求,springmvc会选择相应的转换器进行处理,看看两种方式下springmvc对转换器的处理情况:
第一种方式,注册实现了HttpMessageConverter接口的转换器:
当springmvc调用该转换器的canRead方法时可以看到如下结果:
第二种方式,注册继承了MappingJackson2HttpMessageConverter类的转换器:
当springmvc调用该转换器的canRead方法时可以看到如下结果:
那么为什么springmvc为两种方式下转换器的canRead方法传递的参数不同呢?springmvc主要在下面的方法中对转换器进行判断并调用:
注册继承了MappingJackson2HttpMessageConverter类的转换器,对该方法进行断点调试:
注册实现了HttpMessageConverter接口的转换器,对该方法进行断点调试:
结论
springmvc根据表达式converter instanceof GenericHttpMessageConverter进行判断,并为不同的转换器converter提供不同的参数。实现了HttpMessageConverter接口的转换器不是GenericHttpMessageConverter的导出类型,因此其canRead方法无法获取具体的泛型类型信息,不适用于包含泛型类型的转型;MappingJackson2HttpMessageConverter或其子类是GenericHttpMessageConverter的导出类型,因而适用于包含泛型类型的转型。判断结束后springmvc会调用转换器中的Read方法,将请求体中的json字符串转换为相应的List集合并返回。
ps:开始我用的是实现了HttpMessageConverter接口的转换器,百度上很多人说仅通过class对象就能获取相应的泛型类型信息,就像下面这样:
List<Long> longs = new ArrayList<Long>();
//目标class对象
Class c = longs.getClass();
//若c对应的对象继承了泛型类
ParameterizedType type = (ParameterizedType) c.getGenericSuperclass();
//若c对应的对象实现了(多个)泛型接口
//ParameterizedType[] type = (ParameterizedType[]) c.getGenericInterfaces();
//输出其泛型参数类型
System.out.println(type.getActualTypeArguments()[0]);
但其输出结果为:E
E表示的是ArrayList直接父类的泛型类型(此处仅为一占位符),因此我认为通过这种方式,只能获取到目标class对象的直接父类(或者其所实现接口)的泛型类型信息,无法获取class对象本身的泛型类型信息。
以上均为本人通过调试或者查资料所得出的结论,若大家发现有不对的地方,希望能够及时指出,对此我表示万分感谢!