@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
@Overridepublic 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 {//是否支持指定MediaType和Class的读操作【从请求获得数据】boolean canRead(Class> clazz, @Nullable MediaType mediaType);//是否支持指定MediaType和Class的写操作【向响应写入数据】boolean canWrite(Class> clazz, @Nullable MediaType mediaType);//获得支持的MediaType内容类型列表List getSupportedMediaTypes();//读,从请求获得数据T read(Class extends T> clazz, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException;//写,向响应写入数据void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException;}
@SuppressWarnings({"rawtypes", "unchecked"})protected 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 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 acceptableTypes = getAcceptableMediaTypes(request);//根据返回结果信息获得可以返回的处理类型List producibleTypes = getProducibleMediaTypes(request, valueType, targetType);//响应结果有数据,但是没有可生成的返回结果处理类型,抛出异常,没有找到转换器去处理响应数据if (body != null && producibleTypes.isEmpty()) {throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);}//待使用的响应结果的内容类型List 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去写响应数据
上一篇:前端基础之CSS扫盲