@RequestBody注解和@ResponseBody注解实现原理源码剖析+自定义消息转换器
@ RequestBody:将请求中的参数json转换为对象 @ResponseBody:将对象转换为json响应给浏览器 @RequestBody和@ResponseBody的解析都发生在SpringMVC处理请求的第四步HandlerAdapter执行Handler逻辑并返回ModelAndView, 此处只贴主要流程代码,全流程代码及处理请求的其他步骤 请参考上篇文章: SpringMVC请求流程处理源码剖析
第四部主要做了三件事情:
- 遍历handler形参,根据不同形参类型以及是否存在@RequestBody、@RequestParam等和注解获取不同的参数解析器,利用参数解析器在请求中获取实参
- 反射调用Handler业务方法,获取返回值
- 根据返回值类型及方法是否存在@ResponseBody获取不同的返回值处理器,利用返回值处理器封装ModelAndView
一、@RequestBody源码分析(完成第四步骤的第一个事情)
首先进入获取handler实参的方法InvocableHandlerMethod#getMethodArgumentValues
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取当前handler方法的形参
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
// 创建新数组,用于封装handler被调用时的实参(不同类型的形参获取方式不一样)
Object[] args = new Object[parameters.length];
// 遍历形参
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
try {
// 根据形参获取实参
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
throw ex;
return args;
}
进入resolveArgument方法,获取对应的参数解析器并获取实参
HandlerMethodArgumentResolverComposite.class
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 根据不同类型的参数获取不同的参数解析器。如:String的参数解析器AbstractNamedValueMethodArgumentResolver、Map的参数解析器MapMethodProcessor、带有@RequestBody注解的参数解析器RequestResponseBodyMethodProcessor等
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
// 利用对应的参数解析器解析形参获取实参
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
进入getArgumentResolver
HandlerMethodArgumentResolverComposite.class
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
// 遍历参数处理器集合根据参数获取合适的参数处理器(适配器模式)处理@RequestBody的参数处理器是RequestResponseBodyMethodProcessor
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
return result;
}
进入supportsParameter,判断参数中是否带有@RequestBody注解如果有则此参数用RequestResponseBodyMethodProcessor解析器解析。
RequestResponseBodyMethodProcessor.class
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
拿到参数解析器后进入RequestResponseBodyMethodProcessor#resolveArgument
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
// 使用消息转换器读取,将请求中的参数读取转换成对象
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
return adaptArgumentIfNecessary(arg, parameter);
}
进入AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
MediaType contentType;
boolean noContentType = false;
try {
// 获取contentType application/json
contentType = inputMessage.getHeaders().getContentType();
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
if (contentType == null) {
noContentType = true;
contentType = MediaType.APPLICATION_OCTET_STREAM;
Class<?> contextClass = parameter.getContainingClass();
// 获取参数类型
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
if (targetClass == null) {
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
targetClass = (Class<T>) resolvableType.resolve();
HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
Object body = NO_VALUE;
EmptyBodyCheckingHttpInputMessage message;
try {
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
// 遍历消息转换器,可以自定义消息转换器只要注入到HandlerAdapter的messageConverters属性
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
// 过滤合适的消息转换器canRead的底层判断逻辑是(该转换器是否包含请求中Content-Type指定的MediaType和改转换器的supports方法是否为true共同决定是否使用此消息转换器解析参数)
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
if (message.hasBody()) {
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
// 使用消息转换器转换消息read(targetClass, msgToUse) targetClass:目标参数类型、msgToUse:包含json的输入流,red的底层方法是readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage)方法用于将数据流转换为指定对象,可在自定义消息转换器重写
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
else {
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
break;
catch (IOException ex) {
throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
if (body == NO_VALUE) {
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
(noContentType && !message.hasBody())) {
return null;
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
MediaType selectedContentType = contentType;
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
return body;
}
二、@ResponseBody源码分析(完成第四步骤的第三个事情)
反射执行完handler方法拿到返回之后进入HandlerMethodReturnValueHandlerComposite#handleReturnValue方法处理返回结果值
HandlerMethodReturnValueHandlerComposite.class
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 根据不同结果类型获取不同的结果处理器如:String类型的结果处理器ViewNameMethodReturnValueHandler、ModelAndView类型的
// 结果处理器ModelAndViewMethodReturnValueHandler、带有@ResponseBody是的结果处理
//RequestResponseBodyMethodProcessor等
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
// 调用结果处理器完成handler返回值与ModelAndViewContainer的关系组装
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
进入RequestResponseBodyMethodProcessor#handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
// 使用消息转换器写入响应Response
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
进入AbstractMessageConverterMethodProcessor#writeWithMessageConverters
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Object body;
Class<?> valueType;
Type targetType;
if (value instanceof CharSequence) {
// 返回值对象
body = value.toString();
valueType = String.class;
targetType = String.class;
else {
body = value;
valueType = getReturnValueType(body, returnType);
// 返回值类型
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
if (isResourceType(value, returnType)) {
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
outputMessage.getServletResponse().getStatus() == 200) {
Resource resource = (Resource) value;
try {
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
body = HttpRange.toResourceRegions(httpRanges, resource);
valueType = body.getClass();
targetType = RESOURCE_REGION_LIST_TYPE;
catch (IllegalArgumentException ex) {
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
MediaType selectedMediaType = null;
// 获取ContentType
MediaType contentType = outputMessage.getHeaders().getContentType();
if (contentType != null && contentType.isConcrete()) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" + contentType + "' in response");
selectedMediaType = contentType;
else {
HttpServletRequest request = inputMessage.getServletRequest();
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException(
"No converter found for return value of type: " + valueType);
List<MediaType> mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
if (mediaTypesToUse.isEmpty()) {
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
if (logger.isDebugEnabled()) {
logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
return;
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
for (MediaType mediaType : mediaTypesToUse) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
if (logger.isDebugEnabled()) {
logger.debug("Using '" + selectedMediaType + "', given " +
acceptableTypes + " and supported " + producibleTypes);
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
// 遍历消息转换器
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
// 过滤合适的消息转换器canWrite的底层判断逻辑是 (该转换器是否包含请求中Content-Type指定的MediaType和改转换器的supports方法是否为true共同决定是否使用此消息转换器解析参数)
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
// 调用消息转换器的write方法底层调用的是writeInternal(T t, HttpOutputMessage outputMessage)
//用于将对象转换为指定格式文本写入Response,outputMessage为响应输出流,可在自定义消息转换器重写
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
return;
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
当使用RequestResponseBodyMethodProcessor处理器处理结果值是步骤四返回的ModelAndView为null,当ModelAndView为null时不执行SpringMVC处理请求的步骤七视图渲染和请求转发过程,所以最终接口响应为消息转换器写入Response输出流中的文本。
三、自定义消息转换器
实现效果: 1.自定义消息转换器MyMessageConverter实现HttpMessageConverter接口专门处理User类型的参数 2.可以name#张三,age#20 转换为User作为参数 3.可以将User转换为 “获取到名称为张三,年龄20岁的人”
1.构建demo工程目录:
DemoController.class
@Controller
@RequestMapping("/demo")
public class DemoController {
@RequestMapping("/handle01")
@ResponseBody
public User handle01(@RequestBody User user) {
System.out.println(user);
return user;
}
User.class
public class User {
private String name;
private Integer age;
public String getName() {
return name;
public void setName(String name) {
this.name = name;
public Integer getAge() {
return age;
public void setAge(Integer age) {
this.age = age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
2.自定义消息转换器,(该转换器是否包含请求中Content-Type指定的MediaType和改转换器的supports方法是否为true共同决定是否使用此消息转换器解析参数)
/**
* 自定义消息转换器
* 注:该转换器是否包含请求中Content-Type指定的MediaType和改转换器的supports方法是否为true共同决定是否使用此消息转换器解析参数
public class MyMessageConverter extends AbstractHttpMessageConverter<User> {
public MyMessageConverter() {
// 新建一个我们自定义的媒体类型application/xxx-lago
super(new MediaType("application", "xxx-lago", Charset.forName("UTF-8")));
@Override
protected boolean supports(Class<?> clazz) {
//表明只处理User类型的参数。
return User.class.isAssignableFrom(clazz);
//重写readlntenal 方法,处理请求的数据。
@Override
protected User readInternal(Class<? extends User> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
String str = StreamUtils.copyToString(inputMessage.getBody(), Charset.forName("UTF-8"));
// 解析参数
String[] split = str.split(",");
String name = split[0].split("#")[1];
String age = split[1].split("#")[1];
User user=new User();
user.setName(name);
user.setAge(Integer.parseInt(age));
return user;
//重写writeInternal ,处理如何输出数据到response。