SpringMVC源码-----@RequestBody和@ResponseBody原理解析
1. 概述
在SpringMVC的使⽤时,往往会⽤到@RequestBody和@ResponseBody两个注解,尤其是处理ajax请求必然要使⽤@ResponseBody 注解。这两个注解对应着Controller⽅法的参数解析和返回值处理,开始时都是只知其⽤,不知原理。我们来看个例⼦。
@RequestMapping("/requestBody")
public void requestBody(@RequestBody String body, Writer writer)throws IOException{
writer.write(body);
}
@RequestMapping(value="/responseBody", produces="application/json")
@ResponseBody
public Map<String, Object>responseBody(){
Map<String, Object> retMap =new HashMap<>();
retMap.put("param1","abc");
return retMap;
}
第⼀个requestBody请求,使⽤@RequestBody将HTTP请求体转换成String类型,第⼆个responseBody请求,将Map对象转换成json 格式输出到HTTP响应中。这两个请求⽅法没有什么特殊,就是⼀个在参数前加了@RequestBody注解,⼀个在⽅法上加了
@ResponseBody注解。⽽这两个注解是怎么完成HTTP报⽂信息同Controller⽅法中对象的转换的呢?
SpringMVC处理请求和响应时,⽀持多种类型的请求参数和返回类型,⽽此种功能的实现就需要对HTTP消息体和参数及返回值进⾏转换,为此SpringMVC提供了⼤量的转换类,所有转换类都实现了HttpMessageConverter接⼝。
public interface HttpMessageConverter<T>{
// 当前转换器是否能将HTTP报⽂转换为对象类型
boolean canRead(Class<?> clazz, MediaType mediaType);
// 当前转换器是否能将对象类型转换为HTTP报⽂
boolean canWrite(Class<?> clazz, MediaType mediaType);
// 转换器能⽀持的HTTP媒体类型
List<MediaType>getSupportedMediaTypes();
// 转换HTTP报⽂为特定类型
T read(Class<?extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
// 将特定类型对象转换为HTTP报⽂
void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
HttpMessageConverter接⼝定义了5个⽅法,⽤于将HTTP请求报⽂转换为java对象,以及将java对象转换为HTTP响应报⽂。
对应到SpringMVC的Controller⽅法,read⽅法即是读取HTTP请求转换为参数对象,write⽅法即是将返回值对象转换为HTTP响应报⽂。SpringMVC定义了两个接⼝来操作这两个过程:参数解析器HandlerMethodArgumentResolver和返回值处理器HandlerMethodReturnValueHandler。
// 参数解析器接⼝
public interface HandlerMethodArgumentResolver {
// 解析器是否⽀持⽅法参数
boolean supportsParameter(MethodParameter parameter);
// 解析HTTP报⽂中对应的⽅法参数
Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)throws Exception;
}
// 返回值处理器接⼝
public interface HandlerMethodReturnValueHandler {
// 处理器是否⽀持返回值类型
boolean supportsReturnType(MethodParameter returnType);
// 将返回值解析为HTTP响应报⽂
void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws Exception;
}
参数解析器和返回值处理器在底层处理时,都是通过HttpMessageConverter进⾏转换。流程如下:
SpringMVC为@RequestBody和@ResponseBody两个注解实现了统⼀处理类RequestResponseBodyMethodProcessor,实现了HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler两个接⼝。
由上⼀篇⽂章我们可以知道,Controller⽅法被封装成ServletInvocableHandlerMethod类,并且由invokeAndHandle⽅法完成请求处理。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
< providedArgs)throws Exception {
// 执⾏请求
Object returnValue =invokeForRequest(webRequest, mavContainer, providedArgs);
// 返回值处理
try{
mvc的urnValueHandlers.handleReturnValue(
returnValue,getReturnValueType(returnValue), mavContainer, webRequest);
}
catch(Exception ex){
if(logger.isTraceEnabled()){
}
throw ex;
}
}
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
< providedArgs)throws Exception {
// 参数解析
Object[] args =getMethodArgumentValues(request, mavContainer, providedArgs);
// invoke Controller⽅法
Object returnValue =doInvoke(args);
return returnValue;
}
在invoke Controller⽅法的前后分别执⾏了⽅法参数的解析和返回值的处理,我们分别来看。
2. 参数解析
private Object[]getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
< providedArgs)throws Exception {
MethodParameter[] parameters =getMethodParameters();
Object[] args =new Object[parameters.length];
// 遍历所有参数,逐个解析
for(int i =0; i < parameters.length; i++){
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i]=resolveProvidedArgument(parameter, providedArgs);
if(args[i]!= null){
continue;
}
// 参数解析器解析HTTP报⽂到参数
if(this.argumentResolvers.supportsParameter(parameter)){
args[i]=solveArgument(
parameter, mavContainer, request,this.dataBinderFactory);
continue;
}
}
return args;
}
getMethodArgumentValues⽅法中的argumentResolvers就是多个HandlerMethodArgumentResolver的集合
体,supportsParameter⽅法寻参数合适的解析器,resolveArgument调⽤具体解析器的resolveArgument⽅法执⾏。
我们从RequestResponseBodyMethodProcessor看看@RequestBody的解析过程。RequestResponseBodyMethodProcessor的supportsParameter定义了它⽀持的参数类型,即必须有@RequestBody注解。
public boolean supportsParameter(MethodParameter parameter){
return parameter.hasParameterAnnotation(RequestBody.class);
}
再来看resolveArgument⽅法
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)throws Exception {
parameter = stedIfOptional();
// 通过HttpMessageConverter读取HTTP报⽂
Object arg =readWithMessageConverters(webRequest, parameter, NestedGenericParameterType()); String name = VariableNameForParameter(parameter);
WebDataBinder binder = ateBinder(webRequest, arg, name);
if(arg != null){
validateIfApplicable(binder, parameter);
BindingResult().hasErrors()&&isBindExceptionRequired(binder, parameter)){
throw new MethodArgumentNotValidException(parameter, BindingResult());
}
}
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, BindingResult());
return adaptArgumentIfNecessary(arg, parameter);
}
具体实现由HttpMessageConverter来完成
protected<T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType)throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
....
try{
inputMessage =new EmptyBodyCheckingHttpInputMessage(inputMessage);
for(HttpMessageConverter<?> converter :ssageConverters){
Class<HttpMessageConverter<?>> converterType =(Class<HttpMessageConverter<?>>) Class();
....
// 判断转换器是否⽀持参数类型
if(converter.canRead(targetClass, contentType)){
Body()!= null){
inputMessage =getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
// read⽅法执⾏HTTP报⽂到参数的转换
body =((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
body =getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
else{
body =getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
}
break;
}
...
}
}
catch(IOException ex){
throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
}
....
return body;
}
代码部分省略了,关键部分即是遍历所有的HttpMessageConverter,通过canRead⽅法判断转换器是否⽀持对参数的转换,然后执⾏read⽅法完成转换。
3.返回值处理
完成Controller⽅法的调⽤后,在ServletInvocableHandlerMethod的invokeAndHandle中,使⽤返回值处理器对返回值进⾏转换。
returnValue,getReturnValueType(returnValue), mavContainer, webRequest);
这⾥的returnValueHandlers也是HandlerMethodReturnValueHandler的集合体HandlerMethodReturnValueHandlerComposite
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws Exception {
// 选择合适的HandlerMethodReturnValueHandler,如果没有⽤@ResposeBody注解和⽤了注解其返回值处理器肯定不同
HandlerMethodReturnValueHandler handler =selectHandler(returnValue, returnType);
if(handler == null){
throw new IllegalArgumentException("Unknown return value type: "+ ParameterType().getName());
}
// 执⾏返回值处理
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
selectHandler⽅法遍历所有HandlerMethodReturnValueHandler,调⽤其supportsReturnType⽅法选择合适的HandlerMethodReturnValueHandler,然后调⽤其handleReturnValue⽅法完成处理。
这⾥还是以RequestResponseBodyMethodProcessor来分析下@ResponseBody的处理,它的具体实现在AbstractMessageConverterMethodProcessor抽象基类中。
public boolean supportsReturnType(MethodParameter returnType){
return(AnnotatedElementUtils.ContainingClass(), ResponseBody.class)||
returnType.hasMethodAnnotation(ResponseBody.class));
}
RequestResponseBodyMethodProcessor要求⽅法上有@ResponseBody注解或者⽅法所在的Controller类上有@ResponseBody的注解。这就是常常⽤@RestController注解代替@Controller注解的原因,因为@RestController注解⾃带@ResponseBody。
handleReturnValue⽅法实际也是调⽤HttpMessageConverter来完成转换处理
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论