SpringMVC-解析@ResponseBody

ServletInvocableHandlerMethod.invokeAndHandle处理完request得到结果后调用returnValueHandlers.handleReturnValue处理返回值。

HandlerMethodReturnValueHandlerComposite.handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)

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

遍历returnValueHandlers,调用HandlerMethodReturnValueHandler.supportsReturnType判断返回值解析器是否支持解析返回值,支持则返回。调用返回值解析器的handleReturnValue解析返回值。@ResponseBody由RequestResponseBodyMethodProcessor处理。

RequestResponseBodyMethodProcessor.supportsReturnType(MethodParameter returnType)

public boolean supportsReturnType(MethodParameter returnType) {
	return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
			returnType.hasMethodAnnotation(ResponseBody.class));
}

判断类或方法上是否有@ResponseBody注解。

RequestResponseBodyMethodProcessor.handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)

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

创建ServletServerHttpRequest和ServletServerHttpResponse。调用writeWithMessageConverters处理返回值。

AbstractMessageConverterMethodProcessor.writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)

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;
		try {
			acceptableTypes = getAcceptableMediaTypes(request);
		}
		catch (HttpMediaTypeNotAcceptableException ex) {
			int series = outputMessage.getServletResponse().getStatus() / 100;
			if (body == null || series == 4 || series == 5) {
				if (logger.isDebugEnabled()) {
					logger.debug("Ignoring error response content (if any). " + ex);
				}
				return;
			}
			throw ex;
		}
		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);
			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(getSupportedMediaTypes(body.getClass()));
	}
}

1、解析返回值类型
2、如果返回值类型是Resource,则进行处理
3、从ServletServerHttpResponse获取contentType,即MediaType,如果不存在则根据返回值类型从HttpServletRequest中选择出contentType。
4、遍历messageConverters,调用消息转换器的canWrite判断消息转换器是否可写入返回值,如果支持则调用消息转换器的write向ServletServerHttpResponse写入消息。如果messageConverter是GenericHttpMessageConverter类型,canWrite是通过MediaType来判断消息转换器是否可写入返回值。其他消息转换器是通过返回值类型和MediaType来判断否可写入返回值。
@ResponseBody标注的方法返回值由MappingJackson2HttpMessageConverter解析。

MappingJackson2HttpMessageConverter的父类AbstractGenericHttpMessageConverter.write(final T t, @Nullable final Type type, @Nullable MediaType contentType,
HttpOutputMessage outputMessage)

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 {
		writeInternal(t, type, outputMessage);
		outputMessage.getBody().flush();
	}
}

1、addDefaultHeaders向HttpOutputMessage的header写入ContentType和ContentLength
2、如果HttpOutputMessage是StreamingHttpOutputMessage类型调用StreamingHttpOutputMessage.setBody。否则调用writeInternal向HttpOutputMessage写入消息。刷新写入的消息到HttpOutputMessage。

AbstractJackson2HttpMessageConverter.writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)

protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
		throws IOException, HttpMessageNotWritableException {

	MediaType contentType = outputMessage.getHeaders().getContentType();
	JsonEncoding encoding = getJsonEncoding(contentType);

	Class<?> clazz = (object instanceof MappingJacksonValue ?
			((MappingJacksonValue) object).getValue().getClass() : object.getClass());
	ObjectMapper objectMapper = selectObjectMapper(clazz, contentType);
	Assert.state(objectMapper != null, "No ObjectMapper for " + clazz.getName());

	OutputStream outputStream = StreamUtils.nonClosing(outputMessage.getBody());
	try (JsonGenerator generator = objectMapper.getFactory().createGenerator(outputStream, encoding)) {
		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 ?
				objectMapper.writerWithView(serializationView) : 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.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);
	}
}

调用objectWriter.writeValue写入消息。

posted @   shigp1  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示