添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
@RequestBody注解和@ResponseBody注解实现原理源码剖析+自定义消息转换器

@RequestBody注解和@ResponseBody注解实现原理源码剖析+自定义消息转换器

@ RequestBody:将请求中的参数json转换为对象 @ResponseBody:将对象转换为json响应给浏览器 @RequestBody和@ResponseBody的解析都发生在SpringMVC处理请求的第四步HandlerAdapter执行Handler逻辑并返回ModelAndView, 此处只贴主要流程代码,全流程代码及处理请求的其他步骤 请参考上篇文章: SpringMVC请求流程处理源码剖析

第四部主要做了三件事情:

  1. 遍历handler形参,根据不同形参类型以及是否存在@RequestBody、@RequestParam等和注解获取不同的参数解析器,利用参数解析器在请求中获取实参
  2. 反射调用Handler业务方法,获取返回值
  3. 根据返回值类型及方法是否存在@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。