@Controller
public class JsonReturnController {
@ResponseBody
@GetMapping("/getPet")
public Pet getPet(){
Pet pet=new Pet();
pet.setAge(5);
pet.setName("lily");
return pet;
}
}
项目启动后 浏览器输入 http://localhost:8080/getPet 。
debug DispatcherServlet组件中其中对返回值处理的方法为
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod
中的public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object… providedArgs方法
try {
//用返回值处理器处理返回的结果
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
step into 进入内部方法继续debug
@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);
}
解析的结果正如归纳的步骤而言
1
:获得返回结果处理器
\textcolor{red}{1:获得返回结果处理器}
1:获得返回结果处理器
查看RequestResponseBodyMethodProcessor的supportsReturnType方法的实现源码如下
public boolean supportsReturnType(MethodParameter returnType) {
//所在类上有@ResponseBody或返回方法上有@ResponseBody注解,将使用RequestResponseBodyMethodProcessor作为返回结果处理器
//getPet()方法上包含@ResponseBody注解
return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class);
}
2
:返回结果处理器处理返回结果
\textcolor{red}{2:返回结果处理器处理返回结果}
2:返回结果处理器处理返回结果
写入响应结果的关键步骤:
-
内容协商(浏览器默认以请求头的方式告知服务器端允许接受的内容类型)
-
服务器根据自身的能力,决定服务器能生产什么样的内容类型的数据
-
SpringMVC会遍历IOC容器底层的消息转换器HttpMessageConverter 看能否处理
-
HttpMessageConverter 是一个处理消息转换的标准接口,不同的内容类型有具体的实现类去处理
public interface HttpMessageConverter<T> {
//是否支持指定MediaType和Class的读操作【从请求获得数据】
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
//是否支持指定MediaType和Class的写操作【向响应写入数据】
boolean canWrite(Class<?> clazz, @Nullable MediaType 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;
}
- 使用消息转换器HttpMessageConverter向响应输出写数据
关键代码逻辑在于writeWithMessageConverters,使用消息转换器来给返回写入返回数据,源码如下:
@SuppressWarnings({"rawtypes", "unchecked"})
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;
//从响应中获得ContentType信息
MediaType contentType = outputMessage.getHeaders().getContentType();
if (contentType != null && contentType.isConcrete()) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" + contentType + "' in response");
}
//响应中ContentType不为空时,以响应体中的ContentType为准【意指当前请求已被处理过,告知服务器应该响应的数据类型】
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<>();
//遍历请求允许接收的类型,去check 所有的可接受的数据类型是否双方匹配
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();
//遍历IOC容器中的消息转换器,去向Response写数据
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
//判断消息转换器是否支持Class类型与响应结果的MediaType类型间的写操作
((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) {
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}
在解析上面这段代码的时候需要,需要了解一个概念内容协商(浏览器在发送请求的时候告知服务器端将接受怎样的返回数据类型,默认通过在请求头中设定Accept头)
获得浏览器最适合返回得数据类型时,需要用==消息转换器==去响应Response中写数据
使用MappingJacksonHttpMessageConveter去写响应数据