文章目录
- 大纲
- 代码
- ServletInvocableHandlerMethod.java
- HandlerMethod.java
- HandlerMethodReturnValueHandlerComposite.java
- ModelAndViewMethodReturnValueHandler.java
- RequestResponseBodyMethodProcessor.java
- AbstractMessageConverterMethodProcessor.java
- HttpMessageConverter.java
- AbstractGenericHttpMessageConverter.java
- AbstractJackson2HttpMessageConverter.java
- ContentNegotiationManager.java
- HeaderContentNegotiationStrategy.java
大纲
响应JSON
主要类:
ServletInvocableHandlerMethod.java
RequestResponseBodyMethodProcessor.java
主要方法:
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
其他类:
HandlerMethod.java
HandlerMethodReturnValueHandlerComposite.java
ModelAndViewMethodReturnValueHandler.java
RequestResponseBodyMethodProcessor.java
AbstractMessageConverterMethodProcessor.java
HttpMessageConverter.java
AbstractGenericHttpMessageConverter.java
AbstractJackson2HttpMessageConverter.java
最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)
内容协商
根据客户端接收能力不同,返回不同媒体类型的数据。
其他类:
ContentNegotiationManager.java
HeaderContentNegotiationStrategy.java
自定义 MessageConverter
SpringMVC的什么功能。一个入口给容器中添加一个 WebMvcConfigurer
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}
}
}
代码
ServletInvocableHandlerMethod.java
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 执行当前请求,断点去到Controller里面的目标方法
// 真正执行了目标方法
// 确定目标方法每一个参数值
// mavContainer中的defaultModel存储了参数的值
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 {
// 处理返回结果,传进去mavContainer中携带了参数的值
// getReturnValueType(returnValue)得到返回值的类型
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
}
HandlerMethod.java
public class HandlerMethod {
public MethodParameter getReturnValueType(@Nullable Object returnValue) {
return new ReturnValueMethodParameter(returnValue);
}
private class ReturnValueMethodParameter extends HandlerMethodParameter {
@Nullable
private final Object returnValue;
public ReturnValueMethodParameter(@Nullable Object returnValue) {
super(-1);
this.returnValue = returnValue;
}
}
}
HandlerMethodReturnValueHandlerComposite.java
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
@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);
}
@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;
}
// 1、返回值处理器判断是否支持这种类型返回值 supportsReturnType
// 2、返回值处理器调用 handleReturnValue 进行处理
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
// 是不是异步返回值
private boolean isAsyncReturnValue(@Nullable Object value, MethodParameter returnType) {
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (handler instanceof AsyncHandlerMethodReturnValueHandler &&
((AsyncHandlerMethodReturnValueHandler) handler).isAsyncReturnValue(value, returnType)) {
return true;
}
}
return false;
}
}
ModelAndViewMethodReturnValueHandler.java
public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
// 返回值是不是ModelAndView
return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
}
}
RequestResponseBodyMethodProcessor.java
/**
* 处理返回值标注了ResponseBody的
*/
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
@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.java
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
implements HandlerMethodReturnValueHandler {
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媒体类型牵扯到内容协商
// 内容协商就是浏览器会以请求头的方式告诉服务器能接受的内容Accept,有权重
MediaType selectedMediaType = null;
// 判断当前响应头中是否已经有确定的媒体类型。MediaType
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();
// acceptableTypes = {ArrayList@8821} size = 5
// 0 = {MediaType@8825} "text/html"
// 1 = {MediaType@8826} "application/xhtml+xml"
// 2 = {MediaType@8827} "image/webp"
// 3 = {MediaType@8828} "application/xml;q=0.9"
// 4 = {MediaType@8829} "*/*;q=0.8"
// 浏览器能接受的内容类型
// 获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
// 0 = {MediaType@8840} "application/json"
// 1 = {MediaType@8841} "application/*+json"
// 2 = {MediaType@8840} "application/json"
// 3 = {MediaType@8842} "application/*+json"
// 4 = {MediaType@8843} "application/xml;charset=UTF-8"
// 5 = {MediaType@8844} "text/xml;charset=UTF-8"
// 6 = {MediaType@8845} "application/*+xml;charset=UTF-8"
// 7 = {MediaType@8846} "application/xml;charset=UTF-8"
// 8 = {MediaType@8847} "text/xml;charset=UTF-8"
// 9 = {MediaType@8848} "application/*+xml;charset=UTF-8"
// 服务器能响应的类型
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException(
"No converter found for return value of type: " + valueType);
}
// mediaTypesToUse存放了我们能写出什么类型的内容数据
// 进行内容协商的最佳匹配媒体类型
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();
// 0 = {ByteArrayHttpMessageConverter@8885}
// 1 = {StringHttpMessageConverter@8886}
// 2 = {StringHttpMessageConverter@8887}
// 3 = {ResourceHttpMessageConverter@8888}
// 4 = {ResourceRegionHttpMessageConverter@8889}
// 5 = {SourceHttpMessageConverter@8890}
// 6 = {AllEncompassingFormHttpMessageConverter@8891}
// 7 = {MappingJackson2HttpMessageConverter@8892}
// 8 = {MappingJackson2HttpMessageConverter@8893}
// 9 = {MappingJackson2XmlHttpMessageConverter@8894}
// 10 = {MappingJackson2XmlHttpMessageConverter@8895}
// SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能将对象转为xml
// 用支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。
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) {
// 最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)
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()));
}
}
protected boolean isResourceType(@Nullable Object value, MethodParameter returnType) {
Class<?> clazz = getReturnValueType(value, returnType);
return clazz != InputStreamResource.class && Resource.class.isAssignableFrom(clazz);
}
protected List<MediaType> getProducibleMediaTypes(
HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {
Set<MediaType> mediaTypes =
(Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
if (!CollectionUtils.isEmpty(mediaTypes)) {
return new ArrayList<>(mediaTypes);
}
List<MediaType> result = new ArrayList<>();
// 遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象(Person)
//客户端需要【application/xml】。服务端能力【10种、json、xml】
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter instanceof GenericHttpMessageConverter && targetType != null) {
if (((GenericHttpMessageConverter<?>) converter).canWrite(targetType, valueClass, null)) {
// 找到支持操作Person的converter,把converter支持的媒体类型统计出来。
result.addAll(converter.getSupportedMediaTypes(valueClass));
}
}
else if (converter.canWrite(valueClass, null)) {
result.addAll(converter.getSupportedMediaTypes(valueClass));
}
}
return (result.isEmpty() ? Collections.singletonList(MediaType.ALL) : result);
}
private List<MediaType> getAcceptableMediaTypes(HttpServletRequest request)
throws HttpMediaTypeNotAcceptableException {
return this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request));
}
}
HttpMessageConverter.java
/**
* 消息转换器
* HttpMessageConverter: 看是否支持将 此 Class类型的对象,转为MediaType类型的数据。
* 例子:Person对象转为JSON。或者 JSON转为Person
* @param <T>
*/
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
return (canRead(clazz, null) || canWrite(clazz, null) ?
getSupportedMediaTypes() : Collections.emptyList());
}
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
AbstractGenericHttpMessageConverter.java
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 {
// type:Person类型写给响应outputMessage
writeInternal(t, type, outputMessage);
outputMessage.getBody().flush();
}
}
}
AbstractJackson2HttpMessageConverter.java
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);
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);
}
}
}
ContentNegotiationManager.java
public class ContentNegotiationManager implements ContentNegotiationStrategy, MediaTypeFileExtensionResolver {
@Override
public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
// 使用请求头的内容策略来解析媒体类型
for (ContentNegotiationStrategy strategy : this.strategies) {
List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);
if (mediaTypes.equals(MEDIA_TYPE_ALL_LIST)) {
continue;
}
return mediaTypes;
}
return MEDIA_TYPE_ALL_LIST;
}
}
HeaderContentNegotiationStrategy.java
public class HeaderContentNegotiationStrategy implements ContentNegotiationStrategy {
@Override
public List<MediaType> resolveMediaTypes(NativeWebRequest request)
throws HttpMediaTypeNotAcceptableException {
// 获取请求头的ACCEPT字段
String[] headerValueArray = request.getHeaderValues(HttpHeaders.ACCEPT);
if (headerValueArray == null) {
return MEDIA_TYPE_ALL_LIST;
}
List<String> headerValues = Arrays.asList(headerValueArray);
try {
List<MediaType> mediaTypes = MediaType.parseMediaTypes(headerValues);
MediaType.sortBySpecificityAndQuality(mediaTypes);
return !CollectionUtils.isEmpty(mediaTypes) ? mediaTypes : MEDIA_TYPE_ALL_LIST;
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotAcceptableException(
"Could not parse 'Accept' header " + headerValues + ": " + ex.getMessage());
}
}
}