本文目录结构:
一、快速了解spring
的HttpMessageConverter
机制
二、结合@RequestBody
解读HttpMessageConverter
解析请求报文机制
三、结合@ResponseBody
解读HttpMessageConverter
解析响应报文机制
四、创建自定义HttpMessageConverter实现参数加解密
一、快速了解spring
的HttpMessageConverter
机制
快速过一遍相关代码(这里只摘取核心的代码,并注释方法作用,其余代码使用【…】省略,感兴趣的话可以自行查看源码)
org.springframework.web.method.support.InvocableHandlerMethod
/**
* 该方法先解析request的请求参数,然后调用具体方法
* 1.getMethodArgumentValues 解析请求参数
* 2.doInvoke调用具体方法
*/
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
/**
* 解析请求参数
* 我们知道http content-type有很多种类型,
* 譬如:text/html; text/xml; application/json; application/x-www-form-urlencoded等(我们也可以定义自己的content-type),
* 不同的content-type对于请求参数的数据结构是不尽相同的,因此spring提供了一些内置的methodArgumentResolver以及messageConverter来处理常见的content-type
*/
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
......
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
......
// 根据请求参数类型判断是否有适配的methodArgumentResolver
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 根据请求参数类型选择对应的methodArgumentResolver,然后解析参数
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
......
}
}
return args;
}
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite
/**
* 根据请求参数类型判断是否有适配的methodArgumentResolver
*/
public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
/**
* 根据请求参数类型选择对应的methodArgumentResolver,然后解析参数
*/
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
// 根据匹配到的methodArgumentResolver,去methodArgumentResolver内去实现具体的参数解析
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
其实核心流程就是根据parameter去匹配resolver,然后将具体的参数解析工作交给匹配到的resolver,resolver再将解析工作委托给converter
二、结合@RequestBody
解读HttpMessageConverter
解析请求报文机制
- 根据parameter匹配resolver
通过阅读第一节核心代码,我们可以知道spring是通过getArgumentResolver方法去获取对应的resolver,具体的逻辑就是遍历所有的resolver实现类(这里包括自定义的resolver),然后通过supportsParameter方法判断是否匹配,如果匹配则返回(说明resolver有优先级,越靠前优先级越高)
通过debug发现,@RequestBody最终匹配到RequestResponseBodyMethodProcessor
类。这一点可以通过该类中的supportsParameter
方法得到证实
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
/**
* 指定该resolver支持的参数类型
* 可以看到该方法判断parameter是否包含@ReqeustBody注解,如果包含则支持,否则不支持
*/
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
- 根据parameter匹配converter
通过debug发现,@RequestBody最终匹配到AbstractJackson2HttpMessageConverter
类。这一点可以通过该类中的canRead
方法得到证实 ,该方法主要是判断当前converter中预设的content-type是否包含request header中的content-type
public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
if (!canRead(mediaType)) {
return false;
}
JavaType javaType = getJavaType(type, contextClass);
ObjectMapper objectMapper = selectObjectMapper(javaType.getRawClass(), mediaType);
if (objectMapper == null) {
return false;
}
AtomicReference<Throwable> causeRef = new AtomicReference<>();
if (objectMapper.canDeserialize(javaType, causeRef)) {
return true;
}
logWarningIfNecessary(javaType, causeRef.get());
return false;
}
3.converter解析参数
上一步中我们知道匹配到的converter是AbstractJackson2HttpMessageConverter
,具体的解析方法是readJavaType
,其实就是将json转换为实体类然后返回,至此就完成了@RequestBody类型参数的解析
/**
* 解析参数
*/
public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
JavaType javaType = getJavaType(type, contextClass);
return readJavaType(javaType, inputMessage);
}
/**
* 将inputMessage中的数据流转换为对应的实体类
*/
private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
MediaType contentType = inputMessage.getHeaders().getContentType();
Charset charset = getCharset(contentType);
ObjectMapper objectMapper = selectObjectMapper(javaType.getRawClass(), contentType);
Assert.state(objectMapper != null, "No ObjectMapper for " + javaType);
boolean isUnicode = ENCODINGS.containsKey(charset.name()) ||
"UTF-16".equals(charset.name()) ||
"UTF-32".equals(charset.name());
try {
if (inputMessage instanceof MappingJacksonInputMessage) {
Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
if (deserializationView != null) {
ObjectReader objectReader = objectMapper.readerWithView(deserializationView).forType(javaType);
if (isUnicode) {
return objectReader.readValue(inputMessage.getBody());
}
else {
Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
return objectReader.readValue(reader);
}
}
}
if (isUnicode) {
return objectMapper.readValue(inputMessage.getBody(), javaType);
}
else {
Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
return objectMapper.readValue(reader, javaType);
}
}
catch (InvalidDefinitionException ex) {
throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
}
catch (JsonProcessingException ex) {
throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex, inputMessage);
}
}
三、结合@ResponseBody
解读HttpMessageConverter
解析响应报文机制
该流程核心逻辑和@RequestBody逻辑类似,只是由read转write,这里就不展开了。
四、创建自定义HttpMessageConverter实现参数加解密
下一篇【自定义HttpMessageConverter实现参数加解密】