http的序列化和反序列化是什么鬼?之前只听说过对象的序列化和反序列化啊。奔着自己很菜的想法去百度了下。下面看一个简单的springmvc的请求:
@RestController
public class PersonController {
@GetMapping("getPerson")
public Person getPerson(@RequestBody Person person) {
System.err.println("create an person: " + person);
return person;
}
}
@RestController中有@ResponseBody,可以帮我们把Person 序列化到resp.body中。@RequestBody可以帮我们把req.body的内容转化为Person 对象。其实,这就是http序列化和反序列化。
Http协议的处理过程,TCP字节流 <---> HttpRequest/HttpResponse <---> 内部对象,就涉及这两种序列化。在springmvc中第一步已经由Servlet容器(tomcat等等)帮我们处理了,第二步则主要由框架帮我们处理。http序列化就是第二步的操作。
哈哈,原来我们一直都在跟http序列化打交道啊,http序列化和反序列化就是请求参数封装为对象和解析对象的过程啊。之前的项目都是以json的形式实现序列化和反序列化的。
将请求的json格式数据封装成了person对象,之后返回person对象,浏览器得到了json数据。这个过程是怎样完成的呢?其实核心是HttpMessageConverter接口;springboot的自动化装配功能将会初始化WebMvcConfigurationSupport类。这个类就是初始化HttpMessageConverter的。WebMvcConfigurationSupport的静态代码块中判断处理类的jar有没有引入,比如
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
是json的实现,springboot默认引入了这个包,所以可以处理json的序列化。如果想添加xml的支持,只需引入xml相关的jar即可。
WebMvcConfigurationSupport类的addDefaultHttpMessageConverters方法默认添加了一些处理类,
有兴趣的可以看下。当放入了这些HttpMessageConverter实现类后,就会处理请求参数了,那么问题来了,放了这么多的实现类究竟谁来处理呢?他根据一定的规则(主要是Content-Type、Accept、controller方法的consumes/produces、Converter.mediaType以及Converter的排列顺序这四个属性)来选择到底是使用哪一个
在http的请求头中有两个参数:Accept:表示浏览器可以接收什么样格式的数据application/json等
Content-Type:表示请求的数据格式是什么样的application/json等
controller方法的consumes对应的是匹配 请求Content-Type的格式
produces则对应Accept格式
下面实现一个处理自定义类型的HttpMessageConverter
1:创建一个实现HttpMessageConverter的类
public class MyMessageConverterextends AbstractHttpMessageConverter {
public MyMessageConverter() {
//设置支持的类型 这里支持的类型是application/x-properties;charset=UTF-8
super(new MediaType("application","x-properties", Charset.forName("UTF-8")));
}
@Override
protected Person readInternal(Class clazz, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException {
//将请求参数封装成person对象 就是请求参数是properties类型的封装成person对象
InputStream inputStream = inputMessage.getBody();
Properties properties =new Properties();
properties.load(new InputStreamReader(inputStream));
String name = properties.getProperty("name");
String phone = properties.getProperty("phone");
Person person =new Person();
person.setName(name);
person.setPhone(phone);
return person;
}
@Override
protected void writeInternal(Person person, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException {
//此方法就是将person对象封装成properties返回给浏览器
Properties properties =new Properties();
properties.setProperty("name", person.getName());
properties.setProperty("phone", person.getPhone());
OutputStream body = outputMessage.getBody();
properties.store(body,"UTF-8");
}
@Override
protected boolean supports(Class clazz) {
return ClassUtils.isAssignable(Person.class,clazz);
}
}
2 将此处理类添加到配置类中
@Configuration
public class MyWebMvcConfigurationSextends WebMvcConfigurationSupport {
@Override
protected void extendMessageConverters(List> converters) {
super.extendMessageConverters(converters);
converters.add(new MyMessageConverter());
}
}
很简单实现WebMvcConfigurationSupport 类的extendMessageConverters方法就可以了。
这样就可以接收properties参数的请求了。
postman发送请求
还有个问题springboot是怎样自动装配这个配置的呢?
@SpringBootApplication注解有个@EnableAutoConfiguration自动装配,进而引入了AutoConfigurationImportSelector.类,此类的作用就是将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。在AutoConfigurationImportSelector类中可以看到通过 SpringFactoriesLoader.loadFactoryNames()把 spring-boot-autoconfigure.jar/META-INF/spring.factories中每一个xxxAutoConfiguration文件都加载到容器中其中就包括WebMvcAutoConfiguration引入EnableWebMvcConfiguration.class 进而初始化DelegatingWebMvcConfiguration此类就是WebMvcConfigurationSupport的实现类。