springboot Controller接口返回流程详解

前言

我们在使用springboot开发rest接口时往往是直接写一个接口,然后返回对象,最后结果就转化为Json格式返回了,本文就探究下这个过程中springmvc是如何完成这个过程的。

源码分析

首先我们写了个最简单的接口,并且返回了一个Test对象

@RestController
public class TestController {

    @GetMapping("/test")
    public Test test() {

        Test test = new Test();
        test.setName("my name");
        return test;
    }

}
@Data
public class Test {

    private String name;

}

首先我们都知道springmvc最终是由DispatcherServlet来调用到业务代码的,这个过程此处不做介绍,不了解的可以参考此文:https://blog.csdn.net/qq_31086797/article/details/107418371

我们直接看DispatcherServlet的doService方法,本文只拉出核心代码,不抓具体细节

@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		......
		try {
			// 核心代码
			doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
				......

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				// 核心代码,处理业务流程
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			......
	}

打断点进入AbstractHandlerMethodAdapter的handle

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

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

进入RequestMappingHandlerAdapter的handleInternal

@Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
		checkRequest(request);

		// Execute invokeHandlerMethod in synchronized block if required.
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// No HttpSession available -> no mutex necessary
				// 核心代码,拿到要处理的方法handlerMethod,继续处理业务
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No synchronization on session demanded at all...
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

		......

		return mav;
	}
@Nullable
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			if (this.argumentResolvers != null) {
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}
			if (this.returnValueHandlers != null) {
				// 核心代码1,returnValueHandlers是个集合,包含了很多内置的处理器,根据不同的返回要求处理不同的返回数据
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}
			invocableMethod.setDataBinderFactory(binderFactory);
			......
			// 核心代码2,继续处理
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}

进入了ServletInvocableHandlerMethod的invokeAndHandle,到这里大概逻辑已经可以看出来了

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		// 核心代码1,处理请求,返回了returnValue,很明显就是我们调用http请求查询到的Controller
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		// 设置返回状态
		setResponseStatus(webRequest);

		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}

		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
			// 核心代码2,处理返回结果
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}
@Nullable
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
		// 处理
		return doInvoke(args);
	}
protected Object doInvoke(Object... args) throws Exception {
		ReflectionUtils.makeAccessible(getBridgedMethod());
		try {
			// 核心代码,这里很明显了,找到要调用的方法,拿到调用的Controller对象,获取到参数,反射调用,然后返回结果
			return getBridgedMethod().invoke(getBean(), args);
		}
		......
	}

这里如何根据http请求找到对应的方法,其实原理也很容易理解,无非就是项目启动时把/url和方法的对应关系保存到ioc容器,在需要使用的时候去查询

至此,我们已经拿到Controller相应的方法调用的结果了,然后我们看handleReturnValue方法。

@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

		// 核心代码1
		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
		// 核心代码2
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}
@Nullable
	private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
		boolean isAsyncValue = isAsyncReturnValue(value, returnType);
		// 循环所有返回值处理器,找可以适配的处理器
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
				continue;
			}
			// 调用每一个处理器的supportsReturnType,直到找到适配的处理器
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}

supportsReturnType最终匹配到的是RequestResponseBodyMethodProcessor处理器,我们看下他的supportsReturnType方法

@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		// 判断类上或者方法上是否存在ResponseBody注解,这里我们类上使用了@RestController注解,@RestController默认是
		// 引用了@ResponseBody的注解的,所以这里就可以适配了
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
				returnType.hasMethodAnnotation(ResponseBody.class));
	}

然后调用RequestResponseBodyMethodProcessor的handleReturnValue方法

@Override
	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.
		// 核心代码
		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());
		}

		......

		MediaType selectedMediaType = null;
		MediaType contentType = outputMessage.getHeaders().getContentType();
		if (contentType != null && contentType.isConcrete()) {
			if (logger.isDebugEnabled()) {
				logger.debug("Found 'Content-Type:" + contentType + "' in response");
			}
			selectedMediaType = contentType;
		}
		......
		// 当前情况下selectedMediaType最终被设置为application/json;q=0.8
		if (selectedMediaType != null) {
			selectedMediaType = selectedMediaType.removeQualityValue();
			// 核心代码1 messageConverters是处理数据的解析器,从所有解析器中找到匹配的处理器
			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)) {
					// 主要是根据canWrite来判断,最终找到MappingJackson2HttpMessageConverter解析器
					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 {
							// 最终找到MappingJackson2HttpMessageConverter解析器,执行MappingJackson2HttpMessageConverter的write
							((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);
		}
	}

进入父类AbstractGenericHttpMessageConverter的write

public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType,
		HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
	final HttpHeaders headers = outputMessage.getHeaders();
	addDefaultHeaders(headers, t, contentType);

	if (outputMessage instanceof StreamingHttpOutputMessage) {
		StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
		streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() {
			@Override
			public OutputStream getBody() {
				return outputStream;
			}
			@Override
			public HttpHeaders getHeaders() {
				return headers;
			}
		}));
	}
	else {
		// 核心代码,write body数据到输出流,里面具体就不细看了
		writeInternal(t, type, outputMessage);
		// flush
		outputMessage.getBody().flush();
	}
}

至此,总流程就走完了,数据提供输出流write出去了

总结

梳理下总体流程如下:

  1. 通过DispatcherServlet根据request的信息找到IOC容器中相应的方法,反射调用
  2. 根据注解或者返回值类型找到对应的返回值处理器RequestResponseBodyMethodProcessor
  3. 找到相应的解析器MappingJackson2HttpMessageConverter,这里也就是默认标记了@ResponseBody注解的Controller方法,最终会使用Jackson来进行json序列化,最终返回给客户端
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot是一个开源的Java框架,用于简化Spring应用程序的开发和部署。ControllerSpring Boot中的一个重要组件,用于处理来自客户端的HTTP请求,并返回相应的响应。 在Spring Boot的Controller中,参数解析是将客户端请求中的参数值转换为Controller方法的参数的过程。Spring Boot支持多种参数解析方式,包括: 1. PathVariable:通过URL路径中的变量值进行参数解析。在Controller方法的参数列表中使用@PathVariable注解来指定变量名称,并通过在URL中使用{变量名}的方式来传递参数值。 2. RequestParam:通过URL中的查询字符串或表单参数进行解析。在Controller方法的参数列表中使用@RequestParam注解来指定参数名称,并通过URL中使用?参数名=参数值的方式来传递参数值。 3. RequestBody:通过请求体中的内容进行解析。在Controller方法的参数列表中使用@RequestBody注解来指定参数类型,并自动将请求体中的内容转换为对应的Java对象。 4. RequestHeader:通过请求头中的参数进行解析。在Controller方法的参数列表中使用@RequestHeader注解来指定参数名称,并根据请求头中的参数值进行解析。 5. CookieValue:通过请求中的Cookie进行解析。在Controller方法的参数列表中使用@CookieValue注解来指定参数名称,并根据请求中的Cookie值进行解析。 上述这些参数解析方式可以灵活地组合使用,在Controller方法的参数列表中可以同时使用多个注解来实现多种参数解析方式。这样可以方便地获取客户端请求中的各种参数值,并进行相应的处理和业务逻辑操作。 总而言之,Spring Boot的Controller中的参数解析功能使得处理客户端请求变得更加简单和灵活,开发者可以根据具体的需求选择合适的参数解析方式,并通过注解来指定参数的名称和类型,从而精确地获取和处理请求中的参数值。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值