详解SpringBoot中添加@ResponseBody注解会发生什么

SpringBoot版本2.2.4.RELEASE。

【1】SpringBoot接收到请求

① springboot接收到一个请求返回json格式的列表,方法参数为JSONObject 格式,使用了注解@RequestBody

为什么这里要说明返回格式、方法参数、参数注解?因为方法参数与参数注解会影响你使用不同的参数解析器与后置处理器!通常使用WebDataBinder进行参数数据绑定结果也不同。

将要调用的目标方法如下:

  @ApiOperation(value="分页查询")
  @RequestMapping(value = "/listPage",method = RequestMethod.POST)
  @ResponseBody
  public ResponseBean listPage(@RequestBody JSONObject params){
    Integer pageNum = params.getInteger("pageNum");
    Integer pageSize = params.getInteger("pageSize");
    String vagueParam = params.getString("vagueParam");
    IPage<TbSysGoodsCategory> indexPage = new Page<>(pageNum, pageSize);
    QueryWrapper<TbSysGoodsCategory> queryWrapper = new QueryWrapper<>();
    if (!StringUtils.isEmpty(vagueParam)){
      queryWrapper.like("name",vagueParam).or().like("code",vagueParam);
    }
    //排序
    queryWrapper.orderByDesc("id");
    indexPage = tbSysGoodsCategoryService.page(indexPage,queryWrapper);
    return new ResponseBean<>(true, indexPage, CommonEnum.SUCCESS_OPTION);
  }

如下所示,首先进入DispatcherServlet使用RequestMappingHandlerAdapter进行处理。

而RequestMappingHandlerAdapter (extends AbstractHandlerMethodAdapter)会调用父类AbstractHandlerMethodAdapter的handle方法进行处理。

AbstractHandlerMethodAdapter.handle方法源码如下:

@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {

	return handleInternal(request, response, (HandlerMethod) handler);
}

可以看到RequestMappingHandlerAdapter还实现了InitializingBean接口,该接口只有一个抽象方法afterPropertiesSet用于在BeanFactory设置完bean属性后执行,具体可参考博文:Spring - bean的初始化和销毁几种实现方式详解

② RequestMappingHandlerAdapter.handleInternal

这里首先在this.checkRequest(request)对请求进行了检测,HttpRequestMethodNotSupportedException异常就是这里抛出的。

//1.检测请求方法是否支持;
//2.检测是否需要session但是没有获取到
	protected final void checkRequest(HttpServletRequest request) throws ServletException {
		// Check whether we should support the request method.
		String method = request.getMethod();
		if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
			throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
		}

		// Check whether a session is required.
		if (this.requireSession && request.getSession(false) == null) {
			throw new HttpSessionRequiredException("Pre-existing session required but none found");
		}
	}

其他没有什么需要特殊说明的,然后直接调用了invokeHandlerMethod方法进行实际业务处理。

【2】RequestMappingHandlerAdapter.invokeHandlerMethod核心处理

RequestMappingHandlerAdapter.invokeHandlerMethod

这个方法十分重要,是请求处理流程中的核心方法。这个方法会根据handlerMethod获取一个ServletInvocableHandlerMethod 并对其进行各种属性设置然后调用其invokeAndHandle方法进行处理。

 @Nullable
  protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  // 对应 2
    ServletWebRequest webRequest = new ServletWebRequest(request, response);

    Object result;
    try {
// 对应 3
      WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
  // 对应 4
      ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
  // 对应 5
      ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
      if (this.argumentResolvers != null) {
        invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
      }

      if (this.returnValueHandlers != null) {
        invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
      }

      invocableMethod.setDataBinderFactory(binderFactory);
      invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
 // 对应 6
      ModelAndViewContainer mavContainer = new ModelAndViewContainer();
      mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
      modelFactory.initModel(webRequest, mavContainer, invocableMethod);
      mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//对应 7
      AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
      asyncWebRequest.setTimeout(this.asyncRequestTimeout);
      WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
      asyncManager.setTaskExecutor(this.taskExecutor);
      asyncManager.setAsyncWebRequest(asyncWebRequest);
      asyncManager.registerCallableInterceptors(this.callableInterceptors);
      asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
      if (asyncManager.hasConcurrentResult()) {
        result = asyncManager.getConcurrentResult();
        mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];
        asyncManager.clearConcurrentResult();
        LogFormatUtils.traceDebug(this.logger, (traceOn) -> {
          String formatted = LogFormatUtils.formatValue(result, !traceOn);
          return "Resume with async result [" + formatted + "]";
        });
        invocableMethod = invocableMethod.wrapConcurrentResult(result);
      }
//这里会跳到【3】ServletInvocableHandlerMethod.invokeAndHandle
      invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);

      if (!asyncManager.isConcurrentHandlingStarted()) {
//这里会跳到【4】RequestMappingHandlerAdapter.getModelAndView
        ModelAndView var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);
        return var15;
      }

      result = null;
    } finally {
    //这里会跳到【5】ServletWebRequest.requestCompleted
      webRequest.requestCompleted();
    }

    return (ModelAndView)result;
  }

① 此时的handlerMethod是什么?

如下图所示,handlermethod里面有bean、创建bean的工厂、bean的类型、原始方法method、桥接方法bridgedMethod以及参数对象parameters等关键属性。

其他都容易理解,那么什么是bridgedMethod?(后续单独分析)

② 此时的ServletWebRequest webRequest是什么?

这个倒是很简单,如下图所示:

③ 此时的WebDataBinderFactory binderFactory

WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);

RequestMappingHandlerAdapter.getDataBinderFactory源码如下:

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
//获取handlerType
		Class<?> handlerType = handlerMethod.getBeanType();
		//根据handlerType 从initBinderCache获取到@InitBinder注解的方法
		Set<Method> methods = this.initBinderCache.get(handlerType);
		//如果initBinderCache中没有,就从handlerType查找@InitBinder注解的方法
		if (methods == null) {
			methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
			this.initBinderCache.put(handlerType, methods);
		}
		List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
		//遍历controllerAdviceBean的方法列表,从适合handlerType中拿到其方法列表
		//然后封装为一个个InvocableHandlerMethod放到initBinderMethods中
		// Global methods first
		this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
			if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
				Object bean = controllerAdviceBean.resolveBean();
				for (Method method : methodSet) {
					initBinderMethods.add(createInitBinderMethod(bean, method));
				}
			}
		});
		for (Method method : methods) {
			Object bean = handlerMethod.getBean();
			initBinderMethods.add(createInitBinderMethod(bean, method));
		}
		return createDataBinderFactory(initBinderMethods);
	}

首先Class<?> handlerType = handlerMethod.getBeanType();通过handlerMethod获取到handlerTYPE,handlerTYPE声明了当前完整类路径以及类上面的注解。其值如下:

然后Set<Method> methods = this.initBinderCache.get(handlerType);尝试先从initBinderCache这个ConcurrentHashMap中获取当前类的使用了InitBinder注解的方法列表。如果methods为空,则从handlerType中获取使用了@InitBinder注解的方法,然后放到initBinderCache中,对应代码this.initBinderCache.put(handlerType, methods);

这个很关键。SpringBoot请求处理流程中最重要的一步就是数据绑定,即将参数写到目标对象上面。那么这里就涉及到参数校验、数据格式转换、绑定结果对象、错误对象等。

最后return createDataBinderFactory(initBinderMethods);其会拿到WebBindingInitializer创建数据绑定工厂,。

protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
			throws Exception {
		return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}

DataBinderFactory其属性ConfigurableWebBindingInitializer对象提供了基础功能,该对象中WebConversionService中转换器实例如下:

④ 根据handlerMethod和binderFactory获取到ModelFactory modelFactory

ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);

RequestMappingHandlerAdapter.getModelFactory方法源码如下:

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
//获取当前handlerMethod对应的handlerType的SessionAttributesHandler
//--如果没有就创建一个new SessionAttributesHandler(handlerType, this.sessionAttributeStore)
//参考④-①
		SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
//获取handlerType
		Class<?> handlerType = handlerMethod.getBeanType();
//获取添加了@ModelAttribute注解的方法
		Set<Method> methods = this.modelAttributeCache.get(handlerType);
		if (methods == null) {
			methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
			this.modelAttributeCache.put(handlerType, methods);
		}
		List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
//从controllerAdviceBean中获取适合当前handlerType的method,
//并封装为一个个InvocableHandlerMethod然后添加到attrMethods
		// Global methods first
		this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
			if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
				Object bean = controllerAdviceBean.resolveBean();
				for (Method method : methodSet) {
					attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
				}
			}
		});
//遍历methods并封装为一个个InvocableHandlerMethod然后添加到attrMethods
		for (Method method : methods) {
			Object bean = handlerMethod.getBean();
			attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
		}
//根据attrMethods, binderFactory, sessionAttrHandler创建一个ModelFactory对象
		return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
	}

可以看到modelFactory主要属性modelMethods、dataBindFactory和sessionAttributeHandler都是为参数绑定数据服务的。

④-① RequestMappingHandlerAdapter.getSessionAttributesHandler获取给定类型的SessionAttributesHandler

方法源码如下:

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
//获取当前handlerMethod对应的handlerType的SessionAttributesHandler
//--如果没有就创建一个new SessionAttributesHandler(handlerType, this.sessionAttributeStore)
//参考④-①
		SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
//获取handlerType
		Class<?> handlerType = handlerMethod.getBeanType();
//获取添加了@ModelAttribute注解的方法
		Set<Method> methods = this.modelAttributeCache.get(handlerType);
		if (methods == null) {
			methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
			this.modelAttributeCache.put(handlerType, methods);
		}
		List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
//从controllerAdviceBean中获取适合当前handlerType的method,
//并封装为一个个InvocableHandlerMethod然后添加到attrMethods
		// Global methods first
		this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
			if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
				Object bean = controllerAdviceBean.resolveBean();
				for (Method method : methodSet) {
					attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
				}
			}
		});
//遍历methods并封装为一个个InvocableHandlerMethod然后添加到attrMethods
		for (Method method : methods) {
			Object bean = handlerMethod.getBean();
			attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
		}
//根据attrMethods, binderFactory, sessionAttrHandler创建一个ModelFactory对象
		return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
	}

④-①-①SessionAttributesHandler.SessionAttributesHandler

构造方法源码如下:

public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
		Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
		this.sessionAttributeStore = sessionAttributeStore;
//尝试从handlerType获取@SessionAttributes注解
		SessionAttributes ann = AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
		if (ann != null) {
		//注解的name属性值放入attributeNames中
			Collections.addAll(this.attributeNames, ann.names());
		//注解的type属性值放入	attributeTypes
			Collections.addAll(this.attributeTypes, ann.types());
		}
		//把所有的attributeNames放入knownAttributeNames
		//在初始化model方法initModel将会使用这些数据
		this.knownAttributeNames.addAll(this.attributeNames);
	}

也就是经过③④两步,创建binderFactory、modelFactory后就会拿到匹配当前handlerMethod的那些@InitBinder、@ModelAttribute的方法(HandlerMethod对象)以及SessionAttributesHandler !这三个东西能做什么?当你为目标方法参数绑定数据的时候就会用到!

⑤ 创建核心处理对象ServletInvocableHandlerMethod invocableMethod并为其属性赋值

ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
   invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
 }

 if (this.returnValueHandlers != null) {
   invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
 }

 invocableMethod.setDataBinderFactory(binderFactory);
 invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);创建ServletInvocableHandlerMethod实例:

protected HandlerMethod(HandlerMethod handlerMethod) {
		Assert.notNull(handlerMethod, "HandlerMethod is required");
		this.bean = handlerMethod.bean;
		this.beanFactory = handlerMethod.beanFactory;
		this.beanType = handlerMethod.beanType;
		this.method = handlerMethod.method;
		this.bridgedMethod = handlerMethod.bridgedMethod;
		this.parameters = handlerMethod.parameters;
		this.responseStatus = handlerMethod.responseStatus;
		this.responseStatusReason = handlerMethod.responseStatusReason;
		this.description = handlerMethod.description;
		this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod;
	}

invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);为invocableMethod设置参数解析器组合对象-HandlerMethodArgumentResolverComposite。其有List<HandlerMethodArgumentResolver> argumentResolversMap<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache两个重要属性。其中具体解析器值列表如下:

我想你现在应该知道为什么方法参数使用@RequestBody就可以进行参数绑定了吧!

继续看returnValueHandlers,也就是返回结果处理器。其中returnValueHandlers是HandlerMethodReturnValueHandlerComposite实例,就像HandlermethodArgumentResolverComposite一样,它包含了所有HandlerMethodReturnValueHandler的列表,并在Spring启动时完成注册。其值列表如下:

ok,我们的主题来了。就是这个RequestResponseBodyMethodProcessor后置处理器对@ResponseBody注解进行的处理!

继续往下走,invocableMethod.setDataBinderFactory(binderFactory);给invocableMethod设置了DataBinderFactory。这个同上都是为数据参数绑定服务,继续往下看invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);parameterNameDiscoverer这里值列表如下:

⑥ 创建mavContainer进行数据的初步处理

//创建ModelAndViewContainer 实例对象
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
//从请求中获取InputFlashMap并把其数据放入defaultModel中,flashmap的作用是在redirect中传递参数
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//调用modelFactory对model进行初始化
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
//重定向时忽略默认Model
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

首先看下ModelAndViewContainer,其核心有三个属性view-视图对象,defaultModel-默认的数据存放地方以及redirectModel-重定向时数据存放地方。

modelFactory.initModel(webRequest, mavContainer, invocableMethod);,这里对model做了处理。也可以说是对目标方法实际调用前对数据做的最后一次处理:

public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
			throws Exception {
	//获取会话属性键值对
		Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);

		//对model中属性-值进行合并处理:称之为补缺更合适
		//如果model中不存在,则放入---这个很重要
		container.mergeAttributes(sessionAttributes);

		//调用标注了@ModelAttribute注解的方法
		invokeModelAttributeMethods(request, container);

		//如果handlerMethod的方法参数标注了@ModelAttribute注解并且在sessionAttributetes存在/或类型匹配,则对其进行遍历
		//尝试获取值,如果获取不到值就会抛出异常;如果获取到值就会放到model-defaultModel中
		for (String name : findSessionAttributeArguments(handlerMethod)) {
			if (!container.containsAttribute(name)) {
				Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
				if (value == null) {
					throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
				}
				container.addAttribute(name, value);
			}
		}
	}

Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);获取会话属性键值对,方法源码如下:

public Map<String, Object> retrieveAttributes(WebRequest request) {
		Map<String, Object> attributes = new HashMap<>();
		//遍历通过@SessionAttributes注解获取的name
		for (String name : this.knownAttributeNames) {
		//从session中获取name对应的值
			Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
			if (value != null) {
			//如果值存在,则放入attributes
				attributes.put(name, value);
			}
		}
		return attributes;
	}

container.mergeAttributes(sessionAttributes);关于ModelMap.mergeAttributes合并属性方法源码如下:

#也就是说遍历sessionAttributes ,如果model中不存在,就放入。如果存在,就跳过!注意,不会进行值覆盖
 public ModelMap mergeAttributes(@Nullable Map<String, ?> attributes) {
    if (attributes != null) {
      attributes.forEach((key, value) -> {
        if (!this.containsKey(key)) {
          this.put(key, value);//this这里值的是modelMap,也就是defaultModel
        }
      });
    }
    return this;
  }

这里 invokeModelAttributeMethods(request, container);调用了@ModelAttribute注解的方法,该方法通常会对model中的值进行更新。从另外一个方面来说呢,类里面的@ModelAttribute方法会在目标方法调用前逐个进行调用!,方法源码如下:

private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
			throws Exception {
//循环调用modelMethod
		while (!this.modelMethods.isEmpty()) {
			InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
			ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
			Assert.state(ann != null, "No ModelAttribute annotation");
			if (container.containsAttribute(ann.name())) {
				if (!ann.binding()) {
					container.setBindingDisabled(ann.name());
				}
				continue;
			}
			//反射调用方法并获取返回值
			Object returnValue = modelMethod.invokeForRequest(request, container);
			//如果返回值不为空,就放入model-(returnValueName, returnValue)
			if (!modelMethod.isVoid()){
				String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
				if (!ann.binding()) {
					container.setBindingDisabled(returnValueName);
				}
				if (!container.containsAttribute(returnValueName)) {
					container.addAttribute(returnValueName, returnValue);
				}
			}
		}
	}

关于findSessionAttributeArguments方法源码如下:

//从方法参数中找到在(@SessionAttributes注解的属性/参数)中存在的或者类型匹配
	 // 且方法参数上标注了@ModelAttribute注解的属性名集合
	private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
		List<String> result = new ArrayList<>();
		for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
			if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
				String name = getNameForParameter(parameter);
				Class<?> paramType = parameter.getParameterType();
				if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
					result.add(name);
				}
			}
		}
		return result;
	}

⑦ 异步请求

这一块先不用管,后续分析

AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

if (asyncManager.hasConcurrentResult()) {
	Object result = asyncManager.getConcurrentResult();
	mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
	asyncManager.clearConcurrentResult();
	LogFormatUtils.traceDebug(logger, traceOn -> {
		String formatted = LogFormatUtils.formatValue(result, !traceOn);
		return "Resume with async result [" + formatted + "]";
	});
	invocableMethod = invocableMethod.wrapConcurrentResult(result);
}

接下来调用invocableMethod.invokeAndHandle(webRequest, mavContainer);就到了ServletInvocableHandlerMethod.invokeAndHandle。

【3】调用目标方法并对返回值进行处理ServletInvocableHandlerMethod.invokeAndHandle

其类继承示意图如下:

ServletInvocableHandlerMethod.invokeAndHandle方法源码如下:

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
//调用目标方法并获取返回值,这里对应 【3.1】 InvocableHandlerMethod.invokeForRequest调用目标方法
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

//设置响应状态
		setResponseStatus(webRequest);

//如果返回值为null,则将mavContainer.RequestHandled设置为true,表示已经处理不需要视图解析
		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				disableContentCachingIfNecessary(webRequest);
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}
//将mavContainer.RequestHandled设置为false
		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");

//返回值进行处理 ,这里对应【3.2】
		try {
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}

关于mavContainer.setRequestHandled(false);源码如下:

public void setRequestHandled(boolean requestHandled) {
		this.requestHandled = requestHandled;
	}

检验请求添加了@ResponseBody注解的方法是否已经处理完,如果处理完则视图解析不再需要。当方法参数有ServletResponse或者OutputStream类型时,同样可以设置这个标识。requestHandled 默认值为false。

【3.1】 InvocableHandlerMethod.invokeForRequest调用目标方法

其方法源码如下所示,结构很清晰:获取方法参数值然后调用目标方法:

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
//解析参数--这里对应 1
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Arguments: " + Arrays.toString(args));
	}
	//根据上面得到的参数值调用目标方法 这里对应 2
	return doInvoke(args);
}

① 解析参数getMethodArgumentValues

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
//获取到方法的参数对象  MethodParameter[]数组
		MethodParameter[] parameters = getMethodParameters();

//如果为空,返回空参数组
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}
		Object[] args = new Object[parameters.length];

		//遍历MethodParameter[] parameters,对每一个方法参数对象获取到具体参数并解析得到参数值
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			//绑定参数名称发现器
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);

			//从providedArgs中尝试获取到参数名
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			//如果方法参数解析器不支持parameter,则抛出异常
			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;
	}

MethodParameter[] parameters = getMethodParameters();这里获取的 MethodParameter[] parameters如下图所示:

参数解析器组合对象( this.resolvers)列表如下所示:

为什么称之为参数解析器组合对象?其实这里的this.resolvers并不是具体的参数解析器而是argumentResolvers、argumentResolverCache组合而成的HandlerMethodArgumentResolverComposite!

可以看到起还有argumentResolverCache属性,其值列表如下:

默认argumentResolverCache是一个容量为256的ConcurrentHashMap,是HandlerMethodArgumentResolverComposite的成员变量:

private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
			new ConcurrentHashMap<>(256);

这个argumentResolverCache是在动态改变,其在判断是否支持paramter的方法中会改变,HandlerMethodArgumentResolverComposite.getArgumentResolver源码如下:

private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
//如果缓存中有,则直接返回
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		//如果缓存中没有就尝试从解析器列表中获取一个支持parameter的,并将解析器 parameter放入缓存
		if (result == null) {
			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
				if (resolver.supportsParameter(parameter)) {
					result = resolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}

为什么要有argumentResolverCache ?你可以没有,但是你就需要每次从argumentResolvers遍历寻找支持当前MethodParameter的参数解析器!之所以保存一份键值对数据到argumentResolverCache ,就是为了下次不用寻找,就是为了更快!

ok ,引申多了。咱们继续回去看如何解析参数获取到参数值!

args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);

这里会调用HandlerMethodArgumentResolverComposite.resolveArgument方法:

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.");
		}
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}

获取的实际的参数解析器如下所示(是RequestResponseBodyMethodProcessor):

调用RequestResponseBodyMethodProcessor.resolveArgument解析参数:

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.");
		}
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}

获取方法参数变量名称String name = Conventions.getVariableNameForParameter(parameter);Conventions.getVariableNameForParameter方法源码如下:

public static String getVariableNameForParameter(MethodParameter parameter) {
		Assert.notNull(parameter, "MethodParameter must not be null");
		Class<?> valueClass;
		boolean pluralize = false;
		String reactiveSuffix = "";
//判断参数类型是不是数组
		if (parameter.getParameterType().isArray()) {
			valueClass = parameter.getParameterType().getComponentType();
			pluralize = true;
		}
		// 判断是不是集合类型
		else if (Collection.class.isAssignableFrom(parameter.getParameterType())) {
			valueClass = ResolvableType.forMethodParameter(parameter).asCollection().resolveGeneric();
			if (valueClass == null) {
				throw new IllegalArgumentException(
						"Cannot generate variable name for non-typed Collection parameter type");
			}
			pluralize = true;
		}
		else {
		//获取参数类型,这里是com.alibaba.fastjson.JSONObject
			valueClass = parameter.getParameterType();
			ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(valueClass);
			if (adapter != null && !adapter.getDescriptor().isNoValue()) {
				reactiveSuffix = ClassUtils.getShortName(valueClass);
				valueClass = parameter.nested().getNestedParameterType();
			}
		}
		String name = ClassUtils.getShortNameAsProperty(valueClass);
		return (pluralize ? pluralize(name) : name + reactiveSuffix);
	}

拿到参数变量名与参数值后,就会进行数据绑定过程。在这个过程中会使用binderFactory创建WebDataBinder对象,然后使用WebBindingInitializer对其进行初始化。

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());
			}
		}

首先我们看一下WebDataBinder实例对象创建过程。DefaultDataBinderFactory.createBinder方法源码如下:

public final WebDataBinder createBinder(
			NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {
//创建WebDataBinder 实例
		WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
		//如果初始化器不为null,进行初始化
		if (this.initializer != null) {
			this.initializer.initBinder(dataBinder, webRequest);
		}
		//这是扩展接口,可以用户自定义以进行更深入的初始化
		initBinder(dataBinder, webRequest);
		return dataBinder;
	}

继续跟createBinderInstance(target, objectName, webRequest);,其会走到ServletRequestDataBinderFactory.createBinderInstance方法,如下所示:

可以发现器创建了一个ExtendedServletRequestDataBinder实例对象,其类继承图如下:

创建ExtendedServletRequestDataBinder实例对象时,一路调用父类的构造方法,最终跟到DataBinder类中:

public DataBinder(@Nullable Object target, String objectName) {
    this.ignoreUnknownFields = true;
    this.ignoreInvalidFields = false;
    this.autoGrowNestedPaths = true;
    this.autoGrowCollectionLimit = 256;
    this.bindingErrorProcessor = new DefaultBindingErrorProcessor();
    this.validators = new ArrayList();
    this.target = ObjectUtils.unwrapOptional(target);
    this.objectName = objectName;
  }

创建完数据绑定器后,就使用初始化器对其进行初始化,ConfigurableWebBindingInitializer.initBinder方法如下所示:

public void initBinder(WebDataBinder binder) {
		binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
		if (this.directFieldAccess) {
			binder.initDirectFieldAccess();
		}
		if (this.messageCodesResolver != null) {
			binder.setMessageCodesResolver(this.messageCodesResolver);
		}
		if (this.bindingErrorProcessor != null) {
			binder.setBindingErrorProcessor(this.bindingErrorProcessor);
		}
		//如果target不为null且校验器不为空,就绑定校验器
		if (this.validator != null && binder.getTarget() != null &&
				this.validator.supports(binder.getTarget().getClass())) {
			binder.setValidator(this.validator);
		}
		//绑定类型转换服务类
		if (this.conversionService != null) {
			binder.setConversionService(this.conversionService);
		}
		if (this.propertyEditorRegistrars != null) {
			for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
				propertyEditorRegistrar.registerCustomEditors(binder);
			}
		}
	}

代码如下所示,初始化完WebDataBinder后,就尝试使用binder的校验器对parameter进行校验(如果参数使用了@Valid注解或者以Valid开头的注解)。校验完后就会获取org.springframework.validation.BeanPropertyBindingResult,如果BeanPropertyBindingResult有错误且你并没有用一个Errors对象的参数接收异常,那么就会抛出MethodArgumentNotValidException异常!

public void initBinder(WebDataBinder binder) {
		binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
		if (this.directFieldAccess) {
			binder.initDirectFieldAccess();
		}
		if (this.messageCodesResolver != null) {
			binder.setMessageCodesResolver(this.messageCodesResolver);
		}
		if (this.bindingErrorProcessor != null) {
			binder.setBindingErrorProcessor(this.bindingErrorProcessor);
		}
		//如果target不为null且校验器不为空,就绑定校验器
		if (this.validator != null && binder.getTarget() != null &&
				this.validator.supports(binder.getTarget().getClass())) {
			binder.setValidator(this.validator);
		}
		//绑定类型转换服务类
		if (this.conversionService != null) {
			binder.setConversionService(this.conversionService);
		}
		if (this.propertyEditorRegistrars != null) {
			for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
				propertyEditorRegistrar.registerCustomEditors(binder);
			}
		}
	}

数据绑定这一块就是参数从形参变为实参的最后一步!如何把请求中的参数值赋给方法的形参,就是通过WebDataBinder 这个对象实现的!可以看下此时binder对象:

有了绑定结果后的binder:

继续往下走mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());把绑定结果放到model中:

OK!回到HandlerMethodArgumentResolverComposite.resolveArgument!

然后继续回到InvocableHandlerMethod.getMethodArgumentValues中:

因为本次请求的目标方法只有一个参数,则其会继续返回到InvocableHandlerMethod.invokeForRequest,也就是说到此,① 已经结束!

② 根据参数值反射调用目标方法

InvocableHandlerMethod.doInvoke方法源码如下:

protected Object doInvoke(Object... args) throws Exception {
//获取桥接方法并使方法可以调用
		ReflectionUtils.makeAccessible(getBridgedMethod());
		try {
//		获取桥接方法以及对应的bean 参数值,然后反射调用
			return getBridgedMethod().invoke(getBean(), args);
		}
		catch (IllegalArgumentException ex) {
			assertTargetBean(getBridgedMethod(), getBean(), args);
			String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
			throw new IllegalStateException(formatInvokeError(text, args), ex);
		}
		catch (InvocationTargetException ex) {
			// Unwrap for HandlerExceptionResolvers ...
			Throwable targetException = ex.getTargetException();
			if (targetException instanceof RuntimeException) {
				throw (RuntimeException) targetException;
			}
			else if (targetException instanceof Error) {
				throw (Error) targetException;
			}
			else if (targetException instanceof Exception) {
				throw (Exception) targetException;
			}
			else {
				throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
			}
		}
	}

这里就会反射调用目标方法进行处理!处理完后会再次返回,一直返回到ServletInvocableHandlerMethod.invokeAndHandle!

到此【3.1】结束!已经调用了目标方法并获取到了目标方法返回值!

【3.2】返回结果处理

ServletInvocableHandlerMethod.invokeAndHandle方法首先会反射调用目标方法,然后拿到方法返回值。最后会根据returnValueHandlers对返回结果进行处理!

this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

这里this.returnValueHandlers同样是一个返回结果处理器组合对象,值列表如下:

① 获取返回结果类型

HandlerMethod.getReturnValueType源码如下:

public MethodParameter getReturnValueType(@Nullable Object returnValue) {
		return new ReturnValueMethodParameter(returnValue);
	}

ReturnValueMethodParameter是HandlerMethod的内部类,并继承自HandlerMethod.HandlerMethodParameter(没错,这货也是HandlerMethod的内部类):

private class ReturnValueMethodParameter extends HandlerMethodParameter {

		@Nullable
		private final Object returnValue;

		public ReturnValueMethodParameter(@Nullable Object returnValue) {
			super(-1);
			this.returnValue = returnValue;
		}

		protected ReturnValueMethodParameter(ReturnValueMethodParameter original) {
			super(original);
			this.returnValue = original.returnValue;
		}

		@Override
		public Class<?> getParameterType() {
			return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
		}

		@Override
		public ReturnValueMethodParameter clone() {
			return new ReturnValueMethodParameter(this);
		}
	}

② 选择HandlerMethodReturnValueHandler

HandlerMethodReturnValueHandlerComposite.handleReturnValue方法源码如下:

private class ReturnValueMethodParameter extends HandlerMethodParameter {

		@Nullable
		private final Object returnValue;

		public ReturnValueMethodParameter(@Nullable Object returnValue) {
			super(-1);
			this.returnValue = returnValue;
		}

		protected ReturnValueMethodParameter(ReturnValueMethodParameter original) {
			super(original);
			this.returnValue = original.returnValue;
		}

		@Override
		public Class<?> getParameterType() {
			return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
		}

		@Override
		public ReturnValueMethodParameter clone() {
			return new ReturnValueMethodParameter(this);
		}
	}

这里returnType如下所示,其是HandlerMethod$ReturnValueMethodParameter对象:

这里寻找到的Handler是RequestResponseBodyMethodProcessor

还记得上面解析参数时,咱们获取到的实际参数解析器也是这个RequestResponseBodyMethodProcessor!

也就是说RequestResponseBodyMethodProcessor就是用来处理@RequestBody和@ResponseBody的!它可以使用HttpMessageConverter从请求中读数据赋给参数,并可以把返回结果扔给响应。HttpMessageConverter在这中间起到了什么作用呢?顾名思义,数据格式转换!

其类结构继承图如下:

③ 返回结果写到outputMessage中

RequestResponseBodyMethodProcessor.handleReturnValue源码如下:

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
	ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
	throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
//设置请求已经被处理
mavContainer.setRequestHandled(true);

//获取一个ServletServerHttpRequest实例-构造函数参数为HttpServletRequest
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
//获取一个 实例ServletServerHttpResponse ,构造函数参数为HttpServletResponse
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

记得在上面解析参数的时候调用过readWithMessageConverters方法,那时是从请求中获取数据。这里返回响应信息需要把返回结果写到响应体中。

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;
//值类型,这里是class com.baby.healthcare.common.ResponseBean
			valueType = getReturnValueType(body, returnType);
			targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
		}
//判断是否Resource 或InputStreamSource
		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;
//响应内容类型
		MediaType contentType = outputMessage.getHeaders().getContentType();
		boolean isContentTypePreset = contentType != null && contentType.isConcrete();
		if (isContentTypePreset) {
			if (logger.isDebugEnabled()) {
				logger.debug("Found 'Content-Type:" + contentType + "' in response");
			}
			selectedMediaType = contentType;
		}
		else {
			HttpServletRequest request = inputMessage.getServletRequest();
			//获取接收的MediaType
			List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
			//获取返回结果的MediaType
			List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
//如果body不为空,但是没有合适的返回结果类型,则抛出异常
			if (body != null && producibleTypes.isEmpty()) {
				throw new HttpMessageNotWritableException(
						"No converter found for return value of type: " + valueType);
			}
//循环比较,从acceptableTypes找到适配producibleTypes的
			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进行排序
			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);
				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) {
							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) {
			Set<MediaType> producibleMediaTypes =
					(Set<MediaType>) inputMessage.getServletRequest()
							.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

			if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
				throw new HttpMessageNotWritableException(
						"No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
			}
			throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
		}
	}

关于MediaType、MimeType与ContType对照表可以参考博文:ContentType与MIME对照表

循环比较,从acceptableTypes找到适配producibleTypes的:

//循环比较,从acceptableTypes找到适配producibleTypes的
			List<MediaType> mediaTypesToUse = new ArrayList<>();
			for (MediaType requestedType : acceptableTypes) {
				for (MediaType producibleType : producibleTypes) {
					if (requestedType.isCompatibleWith(producibleType)) {
						mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
					}
				}
			}

请求接收的内容类型与返回响应的内容类型如下所示:

MediaType.sortBySpecificityAndQuality(mediaTypesToUse);对mediaTypesToUse进行排序,排序后的效果如下所示:

寻找合适的转换器把body写到outputMessage中:

for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
			(GenericHttpMessageConverter<?>) converter : null);
	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) {
				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;
	}
}

这里遍历的messageConverters如下所示:

genericConverter.write(body, targetType, selectedMediaType, outputMessage);最后使用MappingJackson2HttpMessageConverter把body写到outputMessage中。其类结构继承示意图如下:

往响应输出流中写完返回结果并flush后就会依次返回,此时【3.2】HandlerMethodReturnValueHandlerComposite.handleReturnValue返回结果处理执行完毕!

然后返回到ServletInvocableHandlerMethod.invokeAndHandle,此时【3】执行完毕!

【4】RequestMappingHandlerAdapter.getModelAndView尝试获取视图对象

RequestMappingHandlerAdapter.getModelAndView方法源码如下:

@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
		ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
//更新model
	modelFactory.updateModel(webRequest, mavContainer);
	//如果请求已经处理完,则直接返回,不会再尝试创建mav
	if (mavContainer.isRequestHandled()) {
		return null;
	}
	ModelMap model = mavContainer.getModel();
	ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
	if (!mavContainer.isViewReference()) {
		mav.setView((View) mavContainer.getView());
	}
	if (model instanceof RedirectAttributes) {
		Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
		HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
		if (request != null) {
			RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
		}
	}
	return mav;
}

modelFactory.updateModel(webRequest, mavContainer);更新model

  • @SessionAttributes注解的属性键值对放到session中;
  • 如果请求没有处理完毕,则尝试更新model中的BindingResult

② 如果请求处理完毕,则直接返回null

如下图所示,在【3.2】-③中handleReturnValue首先将requestHandled设置为true。那么自然不会往下走去获取视图名并尝试解析

【5】ServletWebRequest.requestCompleted

ServletWebRequest类继承示意图如下:

其会直接调用AbstractRequestAttributes.requestCompleted,方法源码如下:

#标记这个请求已经被完成
##调用所有的销毁回调方法
##更新请求过程中访问到的会话属性
public void requestCompleted() {
		executeRequestDestructionCallbacks();
		updateAccessedSessionAttributes();
		this.requestActive = false;
	}

AbstractRequestAttributes.executeRequestDestructionCallbacks源码如下,其会遍历requestDestructionCallbacks并依次执行每个Runnable。

private void executeRequestDestructionCallbacks() {
//这里使用synchronized 保证每个runnable 只被调用一次
	synchronized (this.requestDestructionCallbacks) {
		for (Runnable runnable : this.requestDestructionCallbacks.values()) {
			runnable.run();
		}
		this.requestDestructionCallbacks.clear();
	}
}

然后依次返回到RequestMappingHandlerAdapter.handleInternal也就是【1】-②:

如果响应头中不包含缓存控制Cache-Control,则尝试对response进行Cache-Control设置:

if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
	if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
			applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
		}
		else {
			prepareResponse(response);
		}
}

然后会返回到AbstractHandlerMethodAdapter.handle方法,然后回到DispatcherServlet.doDispatch,这是获取到的MV为null。

【6】DispatcherServlet剩下的处理

① applyDefaultViewName(processedRequest, mv);尝试获取视图名字

源码如下所示,这里MV为null,自然不存在view name。

private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
		if (mv != null && !mv.hasView()) {
			String defaultViewName = getDefaultViewName(request);
			if (defaultViewName != null) {
				mv.setViewName(defaultViewName);
			}
		}
	}

② mappedHandler.applyPostHandle(processedRequest, response, mv);方法后置处理

其实就是执行拦截器的后置方法postHandle,HandlerExecutionChain.applyPostHandle源码如下:

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
			throws Exception {

		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = interceptors.length - 1; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				interceptor.postHandle(request, response, this.handler, mv);
			}
		}
	}

③ processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

如果存在异常,则MV为 ((ModelAndViewDefiningException) exception).getModelAndView();然后进行render(mv, request, response);;

如果不存在异常,且MV不为null,则进行render(mv, request, response);;

如果MV不存在,则不会进行render(mv, request, response);;,其会直接调用mappedHandler.triggerAfterCompletion(request, response, null);

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		boolean errorView = false;

		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("No view rendering, null ModelAndView returned.");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			// Exception (if any) is already handled..
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

拦截器的完成方法afterCompletion调用,HandlerExecutionChain.triggerAfterCompletion方法源码如下:

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
		throws Exception {

	HandlerInterceptor[] interceptors = getInterceptors();
	if (!ObjectUtils.isEmpty(interceptors)) {
		for (int i = this.interceptorIndex; i >= 0; i--) {
			HandlerInterceptor interceptor = interceptors[i];
			try {
				interceptor.afterCompletion(request, response, this.handler, ex);
			}
			catch (Throwable ex2) {
				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
			}
		}
	}
}

最后执行finally 里面的逻辑:

if (asyncManager.isConcurrentHandlingStarted()) {
	// Instead of postHandle and afterCompletion
	if (mappedHandler != null) {
		mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
	}
}else {
	// Clean up any resources used by a multipart request.
	if (multipartRequestParsed) {
		cleanupMultipart(processedRequest);
	}
}

到此这篇关于详解SpringBoot中添加@ResponseBody注解会发生什么的文章就介绍到这了,更多相关SpringBoot添加@ResponseBody内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot ResponseBody返回值处理的实现

    1. SpringBoot ResponseBody 返回值中null值处理 @PostMapping(path = "/test", produces = MediaType.APPLICATION_JSON_VALUE) public Object test() { JSONObject jsonObject = new JSONObject(); jsonObject.put("test","test"); jsonObject.put(&

  • SpringBoot使用@ResponseBody返回图片的实现

    以前使用HttpServletResponse可以通过输出流的方式来向前台输出图片.现在大部分都是使用springboot,在使用springboot之后,我们应该如何来修改代码呢? Spring Boot项目搭建配置略过,可直接从官网简历一个demo 首先写一个Controller类,包括一个方法,如下: package com.example.demo.common; import org.springframework.http.MediaType; import org.springfr

  • 详解SpringBoot定制@ResponseBody注解返回的Json格式

     1.引言 在SpringMVC的使用中,后端与前端的交互一般是使用Json格式进行数据传输,SpringMVC的@ResponseBody注解可以很好的帮助我们进行转换,但是后端返回数据给前端往往都有约定固定的格式,这时候我们在后端返回的时候都要组拼成固定的格式,每次重复的操作非常麻烦. 2.SpringMVC对@ResponseBody的处理 SpringMVC处理@ResponseBody注解声明的Controller是使用默认的.RequestResponseBodyMethodProc

  • 详解SpringBoot中添加@ResponseBody注解会发生什么

    SpringBoot版本2.2.4.RELEASE. [1]SpringBoot接收到请求 ① springboot接收到一个请求返回json格式的列表,方法参数为JSONObject 格式,使用了注解@RequestBody 为什么这里要说明返回格式.方法参数.参数注解?因为方法参数与参数注解会影响你使用不同的参数解析器与后置处理器!通常使用WebDataBinder进行参数数据绑定结果也不同. 将要调用的目标方法如下: @ApiOperation(value="分页查询") @Re

  • 详解SpringBoot中@NotNull,@NotBlank注解使用

    目录 一.添加依赖 二.在类中使用验证注解 1.创建验证实体类(嵌套使用) 2.创建全局异常处理器,对message信息进行处理,并返回给前端 3.在controller中的使用 三.在方法参数中使用验证注解,与@RequsetParam注解同时使用,注意类上使用@Validated 四.自定义验证注解 一.添加依赖 <!-- spring-boot 2.3及以上的版本只需要引入下面的依赖 --> <dependency> <groupId>org.springfram

  • 详解SpringBoot中@ConditionalOnClass注解的使用

    目录 一.@ConditionalOnClass注解初始 二.@ConditionalOnClass注解用法 1.使用value属性 2.使用name属性 三.@ConditionalOnClass是怎么实现的 四.总结 今天给大家带来的是springboot中的@ConditionalOnClass注解的用法.上次的@ConditionalOnBean注解还记得吗? 一.@ConditionalOnClass注解初始 看下@CodidtionalOnClass注解的定义, 需要注意的有两点,

  • 详解SpringBoot中的参数校验(项目实战)

    Java后端发工作中经常会对前端传递过来的参数做一些校验,在业务中还要抛出异常或者不断的返回异常时的校验信息,充满了if-else这种校验代码,在代码中相当冗长.例如说,用户注册时,会校验手机格式的正确性,用户名的长度等等.虽说前端也可以做参数校验,但是为了保证我们API接口的可靠性,以保证最终数据入库的正确性,后端进行参数校验不可忽视. Hibernate Validator 提供了一种统一方便的方式,让我们快速的实现参数校验. Hibernate Validator 使用注解,实现声明式校验

  • 详解SpringBoot之添加单元测试

    本文介绍了详解SpringBoot之添加单元测试,分享给大家,希望此文章对各位有所帮助 在SpringBoot里添加单元测试是非常简单的一件事,我们只需要添加SpringBoot单元测试的依赖jar,然后再添加两个注解就可搞定了. 首先我们来添加单元测试所需要的jar <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test<

  • 详解SpringBoot中的tomcat优化和修改

    项目背景 在做项目的时候,把SpringBoot的项目打包成安装包了,在客户上面安装运行,一切都是那么的完美,可是发生了意外,对方突然说导出导入的文件都不行了.我急急忙忙的查看日志,发现报了一个错误 java.io.IOException: The temporary upload location [C:\Windows\Temp\tomcat.1351070438015228346.8884\work\Tomcat\localhost\ROOT] is not valid at org.ap

  • 详解SpringBoot中自定义和配置拦截器的方法

    目录 1.SpringBoot版本 2.什么是拦截器 3.工作原理 4.拦截器的工作流程 4.1正常流程 4.2中断流程 5.应用场景 6.如何自定义一个拦截器 7.如何使其在Spring Boot中生效 8.实际使用 8.1场景模拟 8.2思路 8.3实现过程 8.4效果体验 9.总结 1.SpringBoot版本 本文基于的Spring Boot的版本是2.6.7 . 2.什么是拦截器 Spring MVC中的拦截器(Interceptor)类似于ServLet中的过滤器(Filter),它

  • 详解SpringBoot中@SessionAttributes的使用

    目录 简介 概述 代码 后端代码 前端代码 测试 简介 说明 本文介绍SpringBoot中@SessionAttributes的用法. 概述 在默认情况下,ModelMap中的属性作用域是request级别,也就是说,当本次请求结束后,ModelMap 中的属性将销毁.如果希望在多个请求中共享ModelMap中的属性,必须将其属性转存到session 中,这样 ModelMap 的属性才可以被跨请求访问. Spring 允许我们有选择地指定 ModelMap 中的哪些属性需要转存到 sessi

  • 详解SpringBoot中如何使用布隆过滤器

    目录 前言 一.Guava 实现布隆过滤器 二.Hutool 布隆过滤器 三.Redission 布隆过滤器 四.小结 五.Guava 布隆过滤器结合 Redis 使用 昨天写了一篇Redis布隆过滤器相关的命令的文章,今天来说一说springboot中如何简单在代码中使用布隆过滤器吧. 目前市面上也有好几种实现方式,如果你需要高度定制化,可以完全从零实现,当然这不是一个简单的工程. 如果只是想快速开始的话,那么市面上现成的实现,无疑是最快的. 前言 今天说到的实现方式有以下几种: 引入 Gua

  • 详解SpringBoot中Controller接收对象列表实现

    如果Spring Boot中对应的Controller要接收一个对象,该对象中又存放了一个List列表,那么页面该如何传递相关应的参数信息呢. 本篇文章给大家一个简单的示例,提供一种实现方式. 实体类 首先看实体类的结构(注意使用了Lombok): @Data public class Rules { private List<Rule> rules; } 对应Rule实体类代码如下: @Data public class Rule { /** * 类名 */ private String c

随机推荐