上篇文章写了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 接口说明
-
public
interface
HandlerMethodArgumentResolver{
/**
-
* 给定的方法参数parameter是否受此解析程序支持。
-
* @param parameter:要检查的方法参数
-
**/
-
booleansupportsParameter(MethodParameterparameter);
-
/**
-
* 将方法参数从给定请求解析为参数值。
-
* @param parameter: 请求参数
-
* @param mavContainer: 容器
-
* @param webRequest: 请求
-
* @param binderFactory: 用于创建一个WebDataBinder用于数据绑定、校验
-
**/
-
Object
resolveArgument
(MethodParameterparameter,@NullableModelAndViewContainermavContainer,
-
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 接口说明
-
public
interface
HttpMessageConverter<T> {
-
boolean
canRead
(Class<?> clazz, @Nullable MediaType mediaType);
-
boolean
canWrite
(Class<?> clazz, @Nullable MediaType mediaType);
-
List<MediaType>
getSupportedMediaTypes
();
-
default List<MediaType>
getSupportedMediaTypes
(Class<?> clazz) {
-
return !
this.canRead(clazz, (MediaType)
null) && !
this.canWrite(clazz, (MediaType)
null) ? Collections.emptyList() :
this.getSupportedMediaTypes();
-
}
-
T
read
(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
-
void
write
(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
-
}
getSupportedMediaTypes:获取支持的MediaType集合(如:text/html,text/plain,application/json)
canRead:判断是否能读,针对请求
read:将请求数据进行格式转换(canRead方法返回值为true时调用)
canWrite:判断是否能写,针对响应
write:将响应数据进行格式转换(canWrite方法返回值为true时调用)
4、自定义HandlerMethodArgumentResolver
自定义的过程基本上就是继承接口,然后加入到系统里
4.1、创建springboot项目
直接跟着指引,下一步就可以完成了,并没有太多的技术含量,这里也不再赘述
4.2、创建自定义HandlerMethodArgumentResolver
-
import com.example.webdemo.domain.po.Person;
-
import org.springframework.core.MethodParameter;
-
import org.springframework.stereotype.Component;
-
import org.springframework.web.bind.support.WebDataBinderFactory;
-
import org.springframework.web.context.request.NativeWebRequest;
-
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
-
import org.springframework.web.method.support.ModelAndViewContainer;
-
-
public
class
PersonArgumentResolver
implements
HandlerMethodArgumentResolver {
-
@Override
-
public
boolean
supportsParameter
(MethodParameter parameter) {
-
return parameter.getParameterType().equals(Person.class);
-
}
-
-
@Override
-
public Object
resolveArgument
(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {
-
String
s
= webRequest.getParameter(
"person");
-
String[] split = s.split(
":");
-
Person
person
=
new
Person();
-
person.setName(split[
0]);
-
person.setAge(split[
1]);
-
return person;
-
}
-
}
Person定义
-
@Data
-
public
class
Person {
-
private String name;
-
private String age;
-
}
4.3、将自定义Resolver加入到系统中
-
@Configuration
-
public
class
MyWebmvcConfiguration
implements
WebMvcConfigurer {
-
@Override
-
public
void
addArgumentResolvers
(List<HandlerMethodArgumentResolver> resolvers) {
-
resolvers.add(
new
PersonArgumentResolver());
-
}
-
}
4.4 测试接口
-
@RestController
-
@RequestMapping
-
public
class
TestController{
-
@RequestMapping("/up")
public
Stringtest1
(Personperson){
-
System.out.println(person);
return
"Hello";
-
}
-
}
测试脚本
-
curl --request GET \
-
--url
'http://localhost:16002/up?person=chongxin: 1'
记得在PersonArgumentResolver里面打个断点哦
5、自定义Converter
5.1、创建springboot项目
直接沿用上面的吧,稍微的删除下
5.2、自定义MessageConverter
-
public
class
SecondHttpMessageConverter
extends
AbstractHttpMessageConverter<Person> {
-
public
SecondHttpMessageConverter
() {
-
super(
new
MediaType(
"application",
"x-xiangcai", Charset.forName(
"UTF-8")));
-
}
-
@Override
-
protected
boolean
supports
(Class<?> clazz) {
-
return Person.class.isAssignableFrom(clazz);
-
}
-
-
@Override
-
protected Person
readInternal
(Class<? extends Person> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
-
String
s
= StreamUtils.copyToString(inputMessage.getBody(), Charset.defaultCharset());
-
String[] split = s.split(
":");
-
Person
person
=
new
Person();
-
person.setName(split[
0]);
-
person.setAge(split[
1]);
-
return person;
-
}
-
@Override
-
protected
void
writeInternal
(Person person, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
-
}
-
}
5.3 加入配置中
-
@Configuration
-
public
class
MyWebmvcConfiguration
implements
WebMvcConfigurer{
-
@Override
-
public
void
extendMessageConverters
(List<HttpMessageConverter<?>>converters){
-
SecondHttpMessageConvertersecondHttpMessageConverter=newSecondHttpMessageConverter();
-
converters.add(secondHttpMessageConverter);
-
}}
5.4 测试代码
-
@RestController
-
public
class
TestController {
-
@RequestMapping("/up")
-
public String
test1
(@RequestBody Person person) {
-
System.out.println(person);
-
return
"Hello";
-
}
-
-
}
注意看这里的代码和上面是不同的,不要使用上面的接口哦
测试脚本
-
curl --request POST \
-
--url http:
//localhost:16002/up \
-
--header
'Content-Type: application/x-xiangcai' \
-
--header
'content-type: text/plain' \
-
--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