飞道的博客

springboot请求参数绑定原理篇

399人阅读  评论(0)

上篇文章写了SpringBoot 参数接收只看这一篇文章就够了,只是写了使用方法,没有写为什么,原理是什么,这篇文章也是之前的预先的计划,稍微花点时间整理下,

知其然知其所以然,才算是能彻底掌握,但是说实在话,都是工具,会用是硬道理。有精力了再去搞原理。

1、原理

百度上看到一张图,还蛮好的,直接拿来用了,侵权删。

Spring容器管理的RequestMappingHandlerAdapter对象会自动帮我们分解参数并组装成所需要的对象。

RequestMappingHandlerAdapter完全按照名称匹配且只能组装在request的参数域中提供参数的对象。

上图我花了两个红色框,一个HttpMessageConverter 和 HandlerMethodArgumentResolver

这两个也是今天的重点。

2、HandlerMethodArgumentResolver

2.1 基础概念

HandlerMethodArgumentResolver 方法参数解析器,是Spring Web(SpringMVC)组件中的众多解析器之一,主要用来对Controller中方法的参数进行处理。

2.2 内置resolver

参数

Resolver

HttpServletRequest

ServletRequestMethodArgumentResolver

HttpServletResponse

ServletResponseMethodArgumentResolver

@RequestParam

RequestParamMapMethodArgumentResolver

@PathVariable

PathVariableMapMethodArgumentResolver

@RequestHeader

RequestHeaderMapMethodArgumentResolver

@RequestBody

RequestResponseBodyMethodProcessor

@ModelAttribute

ModelAttributeMethodProcessor

@RequestPart

RequestPartMethodArgumentResolver

@CookieValue

ServletCookieValueMethodArgumentResolver

HttpEntity/RequestEntity

HttpEntityMethodProcessor

2.3 接口说明


   
  1. public interface HandlerMethodArgumentResolver{ /**
  2. * 给定的方法参数parameter是否受此解析程序支持。
  3. * @param parameter:要检查的方法参数
  4. **/
  5. booleansupportsParameter(MethodParameterparameter);
  6. /**
  7. * 将方法参数从给定请求解析为参数值。
  8. * @param parameter: 请求参数
  9. * @param mavContainer: 容器
  10. * @param webRequest: 请求
  11. * @param binderFactory: 用于创建一个WebDataBinder用于数据绑定、校验
  12. **/
  13. Object resolveArgument (MethodParameterparameter,@NullableModelAndViewContainermavContainer,
  14. NativeWebRequestwebRequest,@NullableWebDataBinderFactorybinderFactory)throwsException;}

3、HttpMessageConverter

3.1 基础概念

负责将请求信息转换为一个对象(类型为 T)

3.2 内置Converter

MappingJackson2HttpMessageConverter 负责读、写JSON格式数据(利用Jackson)

AllEncompassingFormHttpMessageConverter 负责读、写Form表单数据

Jaxb2RootElementHttpMessageConverter 负责读、写XML格式数据(使用JAXB)

ByteArrayHttpMessageConverter 负责读、写二进制格式数据

StringHttpMessageConverter 负责读、写字符串格式数据

ResourceHttpMessageConverter 负责读、写资源文件数据

SourceHttpMessageConverter 负责读、写资源数据

3.3 接口说明


   
  1. public interface HttpMessageConverter<T> {
  2. boolean canRead (Class<?> clazz, @Nullable MediaType mediaType);
  3. boolean canWrite (Class<?> clazz, @Nullable MediaType mediaType);
  4. List<MediaType> getSupportedMediaTypes ();
  5. default List<MediaType> getSupportedMediaTypes (Class<?> clazz) {
  6. return ! this.canRead(clazz, (MediaType) null) && ! this.canWrite(clazz, (MediaType) null) ? Collections.emptyList() : this.getSupportedMediaTypes();
  7. }
  8. T read (Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;
  9. void write (T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
  10. }

getSupportedMediaTypes:获取支持的MediaType集合(如:text/html,text/plain,application/json)

canRead:判断是否能读,针对请求

read:将请求数据进行格式转换(canRead方法返回值为true时调用)

canWrite:判断是否能写,针对响应

write:将响应数据进行格式转换(canWrite方法返回值为true时调用)

4、自定义HandlerMethodArgumentResolver

自定义的过程基本上就是继承接口,然后加入到系统里

4.1、创建springboot项目

直接跟着指引,下一步就可以完成了,并没有太多的技术含量,这里也不再赘述

4.2、创建自定义HandlerMethodArgumentResolver


   
  1. import com.example.webdemo.domain.po.Person;
  2. import org.springframework.core.MethodParameter;
  3. import org.springframework.stereotype.Component;
  4. import org.springframework.web.bind.support.WebDataBinderFactory;
  5. import org.springframework.web.context.request.NativeWebRequest;
  6. import org.springframework.web.method.support.HandlerMethodArgumentResolver;
  7. import org.springframework.web.method.support.ModelAndViewContainer;
  8. public class PersonArgumentResolver implements HandlerMethodArgumentResolver {
  9. @Override
  10. public boolean supportsParameter (MethodParameter parameter) {
  11. return parameter.getParameterType().equals(Person.class);
  12. }
  13. @Override
  14. public Object resolveArgument (MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
  15. String s = webRequest.getParameter( "person");
  16. String[] split = s.split( ":");
  17. Person person = new Person();
  18. person.setName(split[ 0]);
  19. person.setAge(split[ 1]);
  20. return person;
  21. }
  22. }

Person定义


   
  1. @Data
  2. public class Person {
  3. private String name;
  4. private String age;
  5. }

4.3、将自定义Resolver加入到系统中


   
  1. @Configuration
  2. public class MyWebmvcConfiguration implements WebMvcConfigurer {
  3. @Override
  4. public void addArgumentResolvers (List<HandlerMethodArgumentResolver> resolvers) {
  5. resolvers.add( new PersonArgumentResolver());
  6. }
  7. }

4.4 测试接口


   
  1. @RestController
  2. @RequestMapping
  3. public class TestController{
  4. @RequestMapping("/up") public Stringtest1 (Personperson){
  5. System.out.println(person); return "Hello";
  6. }
  7. }

测试脚本


   
  1. curl --request GET \
  2. --url 'http://localhost:16002/up?person=chongxin: 1'

记得在PersonArgumentResolver里面打个断点哦

5、自定义Converter

5.1、创建springboot项目

直接沿用上面的吧,稍微的删除下

5.2、自定义MessageConverter


   
  1. public class SecondHttpMessageConverter extends AbstractHttpMessageConverter<Person> {
  2. public SecondHttpMessageConverter () {
  3. super( new MediaType( "application", "x-xiangcai", Charset.forName( "UTF-8")));
  4. }
  5. @Override
  6. protected boolean supports (Class<?> clazz) {
  7. return Person.class.isAssignableFrom(clazz);
  8. }
  9. @Override
  10. protected Person readInternal (Class<? extends Person> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
  11. String s = StreamUtils.copyToString(inputMessage.getBody(), Charset.defaultCharset());
  12. String[] split = s.split( ":");
  13. Person person = new Person();
  14. person.setName(split[ 0]);
  15. person.setAge(split[ 1]);
  16. return person;
  17. }
  18. @Override
  19. protected void writeInternal (Person person, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
  20. }
  21. }

5.3 加入配置中


   
  1. @Configuration
  2. public class MyWebmvcConfiguration implements WebMvcConfigurer{
  3. @Override
  4. public void extendMessageConverters (List<HttpMessageConverter<?>>converters){
  5. SecondHttpMessageConvertersecondHttpMessageConverter=newSecondHttpMessageConverter();
  6. converters.add(secondHttpMessageConverter);
  7. }}

5.4 测试代码


   
  1. @RestController
  2. public class TestController {
  3. @RequestMapping("/up")
  4. public String test1 (@RequestBody Person person) {
  5. System.out.println(person);
  6. return "Hello";
  7. }
  8. }

注意看这里的代码和上面是不同的,不要使用上面的接口哦

测试脚本


   
  1. curl --request POST \
  2. --url http: //localhost:16002/up \
  3. --header 'Content-Type: application/x-xiangcai' \
  4. --header 'content-type: text/plain' \
  5. --data 'chongxin: 1'

注意这里使用的是post,并且设置了Content-Type: application/x-xiangcai

5.5 注意点

处理过程会按集合顺序匹配合适的消息转换器,如果有合适的,就会使用该消息转换器处理(读、写),后续的消息转换器不再执行。

自定义的消息转换器要想生效,必须放到集合中相同类型的消息转换器前面,原因参考第二点。

WebMvcConfigurer.configureMessageConverters方法会覆盖默认消息转换器集合

WebMvcConfigurer.extendMessageConverters方法不会覆盖默认消息转换器集合

6、总结

6.1 converter和Resolver的区别:

Converter 主要是用来做数据body的计息,针对@RequestBody

Resolver 主要是用来做数据类型转换,主要是用来解析参数,针对基于键值对的

6.2 自定义套路

继承相应的接口,在Configuration加入列表中

6.3 调用顺序

从数据流图中可以看到先是使用resolver,然后调用converter

6.4 最后贴下debug的图

方便你找到断点,贯穿整个流程

赠人玫瑰,手留余香,感谢点赞


转载:https://blog.csdn.net/perfect2011/article/details/128796835
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场