掘金 后端 ( ) • 2024-04-01 15:52

[springboot源码探索]返回值处理

开始处理返回值

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {

    // ...
    // 返回值处理器组(组合模式,可以理解为一组返回值处理器)
	private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
    
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
 
        // 执行请求 拿到返回值
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		setResponseStatus(webRequest);

		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.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
            // 调用返回值处理器组处理返回值
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}
    // ...
}

返回值处理器组

public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
    // ...
    private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
    
    @Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

        // 从返回值处理器中选择能处理的处理器
		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}
    
    private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
		boolean isAsyncValue = isAsyncReturnValue(value, returnType);
        // 责任链模式 遍历所有的返回值处理器 找到支持的
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
				continue;
			}
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}
}

返回值处理器

public interface HandlerMethodReturnValueHandler {

    // 是否支持要处理的返回值类型
   boolean supportsReturnType(MethodParameter returnType);

    // 处理返回值
   void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
         ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}

springboot中默认的返回值处理器

  1. org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler@28072124,
  2. org.springframework.web.method.annotation.ModelMethodProcessor@7073c17f,
  3. org.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler@30172c70,
  4. org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler@2602dbd7,
  5. org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler@379dc032,
  6. org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@598b8787,
  7. org.springframework.web.servlet.mvc.method.annotation.HttpHeadersReturnValueHandler@50f6546a,
  8. org.springframework.web.servlet.mvc.method.annotation.CallableMethodReturnValueHandler@609ae1c0,
  9. org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler@39df31b1,
  10. org.springframework.web.servlet.mvc.method.annotation.AsyncTaskMethodReturnValueHandler@3c6d3eb2,
  11. org.springframework.web.method.annotation.ModelAttributeMethodProcessor@5eda6851,
  12. org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@697906ab,
  13. org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler@29ebf08c,
  14. org.springframework.web.method.annotation.MapMethodProcessor@7c26c3ad,
  15. org.springframework.web.method.annotation.ModelAttributeMethodProcessor@71b3b498

以处理ResponseBody注解的返回值处理器为例探索返回值处理过程

image-20240401094812784

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
    // ... 
    public boolean supportsReturnType(MethodParameter returnType) {
        // 类上有ResponseBody注解或者方法上有ResponseBody注解
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
				returnType.hasMethodAnnotation(ResponseBody.class));
	}
    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);
	}
    // ...
}
    
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
      implements HandlerMethodReturnValueHandler {
    // ...
    protected final List<HttpMessageConverter<?>> messageConverters;
    
    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;
        // 判断响应里面有没有媒体类型(比如拦截器之类的给写了响应头)
		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();
            // 客户端能接受的类容类型
			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);

            // 给selectedMediaType赋值(找到最佳媒体类型)
			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();
            // 遍历所有的消息转换器 找到可以将指定类型转为媒体类型的消息转换器(例如将Person类型对象转为json字符串) 责任链模式
			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)) {
                    // 返回值 比如person对象
					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) {
			if (isContentTypePreset) {
				throw new HttpMessageNotWritableException(
						"No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
			}
			throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
		}
	}
    // ...
}

内容协商

客户端以请求头的形式告诉服务器它能接受什么样的响应数据,和返回值处理器能生产的对比,找到返回值处理器能生产的,客户端能接受的媒体类型

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7

消息转换器

public interface HttpMessageConverter<T> {

    // 是否支持 将class 以mediaType的形式读进来
   boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

    // 是否支持将clazz以mediaType的形式写出去,例如能否将Person对象转为json类型的数据
   boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);

    // 每种消息转换器都有自己支持的类型
   List<MediaType> getSupportedMediaTypes();

   T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
         throws IOException, HttpMessageNotReadableException;

   void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
         throws IOException, HttpMessageNotWritableException;

}

springboot中默认的消息转换器

  1. org.springframework.http.converter.ByteArrayHttpMessageConverter@1ad1c363,
  2. org.springframework.http.converter.StringHttpMessageConverter@6b2aafbc,
  3. org.springframework.http.converter.StringHttpMessageConverter@446b64b3,
  4. org.springframework.http.converter.ResourceHttpMessageConverter@35ac9ebd,
  5. org.springframework.http.converter.ResourceRegionHttpMessageConverter@56c0a61e,
  6. org.springframework.http.converter.xml.SourceHttpMessageConverter@421ead7e,
  7. org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter@5dcf0772,
  8. org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@50cbcca7,
  9. org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@c472300,
  10. org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter@5f6494c0

MappingJackson2HttpMessageConverter

继承体系

image-20240401090902746

public class MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
    // ...
    public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        //每种转换器都有自己支持的类型, MappingJackson2HttpMessageConverter 支持[application/json, application/*+json]
        super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
	}
    // ...
}
public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {
    // ...
    @Override
	protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException {

		MediaType contentType = outputMessage.getHeaders().getContentType();
		JsonEncoding encoding = getJsonEncoding(contentType);
		JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
		try {
			writePrefix(generator, object);

			Object value = object;
			Class<?> serializationView = null;
			FilterProvider filters = null;
			JavaType javaType = null;

			if (object instanceof MappingJacksonValue) {
				MappingJacksonValue container = (MappingJacksonValue) object;
				value = container.getValue();
				serializationView = container.getSerializationView();
				filters = container.getFilters();
			}
			if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
				javaType = getJavaType(type, null);
			}

			ObjectWriter objectWriter = (serializationView != null ?
					this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer());
			if (filters != null) {
				objectWriter = objectWriter.with(filters);
			}
			if (javaType != null && javaType.isContainerType()) {
				objectWriter = objectWriter.forType(javaType);
			}
			SerializationConfig config = objectWriter.getConfig();
			if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
					config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
				objectWriter = objectWriter.with(this.ssePrettyPrinter);
			}
            // 通过objectWriter将返回值写入到响应留中
            
			objectWriter.writeValue(generator, value);

			writeSuffix(generator, object);
			generator.flush();
		}
		catch (InvalidDefinitionException ex) {
			throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
		}
		catch (JsonProcessingException ex) {
			throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);
		}
	}
    // ...
}
public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T> {
    // ...
    private List<MediaType> supportedMediaTypes = Collections.emptyList();
    // 通过构造方法给supportedMediaTypes赋值,例如MappingJackson2HttpMessageConverter的 super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
    protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper, MediaType... supportedMediaTypes) {
		this(objectMapper);
		setSupportedMediaTypes(Arrays.asList(supportedMediaTypes));
	}
    protected boolean canWrite(@Nullable MediaType mediaType) {
        // MediaType.ALL即*/*
		if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) {
			return true;
		}
		for (MediaType supportedMediaType : getSupportedMediaTypes()) {
			if (supportedMediaType.isCompatibleWith(mediaType)) {
				return true;
			}
		}
		return false;
	}
    // ...
}
    
public abstract class AbstractGenericHttpMessageConverter<T> extends AbstractHttpMessageConverter<T>
		implements GenericHttpMessageConverter<T> {
    // ...
    	@Override
	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 {
            // 将消息写入到响应留中,t为返回值,type,返回值的类型
			writeInternal(t, type, outputMessage);
			outputMessage.getBody().flush();
		}
	}
    protected abstract void writeInternal(T t, @Nullable Type type, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;
    // ...
}

image-20240401105910957

小结

返回值处理核心可以分成三部分

  1. 找到合适的返回值处理器(一个返回值处理器里面有多个消息转换器)
  2. 找到合适的消息转换器(在寻找消息转换器的时候用到了内容协商,客户端能接受什么样的媒体类型,服务器能生产什么样的媒体类型,找到一个最合适的浏览器能接受的,服务器能生产的媒体类型,然后遍历返回值处理器中的消息转换器,看看那个能支持内容协商找到的媒体类型)
  3. 用消息转换器将返回值写入到响应中