本文分析 Spring 注解驱动的实现原理,包括请求匹配、参数解析、结果处理三个方面。

  • 请求匹配:核心注解 @RequestMapping。核心实现 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter。
  • 参数解析:包括 @RequestBody/@RequestParam/@PathVariable/@CookieValue 等注解。核心接口 HandlerMethodArgumentResolver。
  • 结果处理:核心注解 @ResponseBody,返回 REST 风格。核心接口 HandlerMethodReturnValueHandler。无论是 @RequestBody 还是 @ResponseBody,最终都是委托 HttpMessageConverter 进行媒体类型转换。

    1. REST 注解驱动规范

    REST 注解驱动有 Servlet 规范和 Spring 自定义两种,Spring 不支持 Servlet REST 规范。

  • Servlet REST 规范注解:(Spring 不支持)javax.ws.rs 包。大致可以分为以下几类:

    • 请求方式:@POST/@GET、@HttpMethod
    • 媒体类型:@Consumes/@Produces
    • 参数解析:@PathParam/@HeaderParam/@CookieParam/@QueryParam/@FormParam
  • SpringMVC 自定义注解:org.springframework.web.bind.annotation 包。
    • 请求匹配:@RequestMapping。包括请求路径、请求方式、媒体类型、请求头、请求参数等匹配方式。相当于 Servlet 中的请求方式和媒体类型的复合注解。
    • 参数解析:@RequestParam/@PathVariable/@CookieValue
    • REST风格:@ResponseBody/@RequestBody。Spring 既要支持传统的 ModelAndView,也要支持 REST 风格,使用 @ResponseBody 自动切换到 REST 风格。

说明:可以看到,Spring 自定义注解规范和 Servlet REST 规范其实都差不多。但 Spring 生态做的比较强大,根本不叼 Serlvet,自己重新实现了一套注解驱动。

2. 请求匹配

请求匹配的核心 API 是 @RequestMapping 注解,处理这个注解的相关类有:

  • RequestMappingHandlerMapping:处理器映射器。保存 <mapping, handler> 的映射关系,可以根据 request 查找对应的 handler。
  • RequestMappingHandlerAdapter:处理器适配器。将 handler 包装成 HandlerAdapter 后执行,进行参数解析结果转换
  • RequestMappingInfo:保存 @RequestMapping 解析后的信息。

    2.1 @RequestMapping

    RequestMappingHandlerMapping 初始化时会解析所有的 @RequestMapping 注解,保存 <mapping, handler> 的映射关系。当收到请求时,会根据 request 从映射关系中查找对应的 handler 执行。

  • 初始化阶段:RequestMappingHandlerMapping 初始化时,调用 initHandlerMethods 方法解析所有的 @RequestMapping 注解。

    • getCandidateBeanNames:获取容器中的所有 beanNames。
    • isHandler:判断是否有 @Controller 或 @RequestMapping 注解。
    • getMappingForMethod:读取该方法或类上的 @RequestMapping 注解,解析为 RequestMappingInfo。
    • createHandlerMethod:将该方法包装成 HandlerMethod。
  • 运行阶段:当收到请求时,会根据 request 从映射关系表中查找对应的 handler 执行。
    • getMatchingCondition:实际上是调用 RequestMappingInfo#getMatchingCondition 逐一匹配 URL、请求方式、媒体类型等。 Spring MVC(二)注解驱动:基于 REST 风格 - 图1

      2.2 Handler 注册

      Spring 为了方便 Handler 查找,注册了根据 mapping 或 url 等多种映射关系。 ```java private final Map> registry = new HashMap<>(); private final Map mappingLookup = new LinkedHashMap<>(); private final MultiValueMap urlLookup = new LinkedMultiValueMap<>(); private final Map> nameLookup = new ConcurrentHashMap<>();

// mapping参数:将 @RequestMapping 注解解析成 RequestMappingInfo // handler参数:method方法所在的实例bean的beanName。执行时需要从容器中获取具体的bean // method参数:具体的的方法 public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); // 1. mappingLookup:RequestMappingInfo -> HandlerMethod this.mappingLookup.put(mapping, handlerMethod);

  1. // 2. urlLookup:url -> RequestMappingInfo
  2. List<String> directUrls = getDirectUrls(mapping);
  3. for (String url : directUrls) {
  4. this.urlLookup.add(url, mapping);
  5. }
  6. // 3. nameLookup:mappingname -> HandlerMethod
  7. String name = null;
  8. if (getNamingStrategy() != null) {
  9. name = getNamingStrategy().getName(handlerMethod, mapping);
  10. addMappingName(name, handlerMethod);
  11. }
  12. // 4. registry:RequestMappingInfo -> MappingRegistration
  13. this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
  14. } finally {
  15. this.readWriteLock.writeLock().unlock();
  16. }

}

  1. <a name="rF5XG"></a>
  2. ## 2.3 Handler 查找
  3. 查找时,先根据 request.url 查找对应的 mapping。如果匹配不到,再匹配所有的 mapping。如果匹配到多个,取优先级最高的那个。所谓的 mapping 匹配,实际上是调用 RequestMappingInfo#getMatchingCondition(request) 方法,也就是我们在 @RequestMapping 上配置的条件。
  4. ```java
  5. protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
  6. List<Match> matches = new ArrayList<>();
  7. List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
  8. // 1. 先根据url匹配
  9. if (directPathMatches != null) {
  10. addMatchingMappings(directPathMatches, matches, request);
  11. }
  12. // 2. 匹配所有的mappings
  13. if (matches.isEmpty()) {
  14. addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
  15. }
  16. // 3. 匹配多个,取优先级最高的那一个
  17. if (!matches.isEmpty()) {
  18. Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
  19. matches.sort(comparator);
  20. Match bestMatch = matches.get(0);
  21. if (matches.size() > 1) {
  22. Match secondBestMatch = matches.get(1);
  23. if (comparator.compare(bestMatch, secondBestMatch) == 0) {
  24. throw new IllegalStateException();
  25. }
  26. }
  27. request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
  28. handleMatch(bestMatch.mapping, lookupPath, request);
  29. return bestMatch.handlerMethod;
  30. // 4. 未匹配到任何handler
  31. } else {
  32. return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
  33. }
  34. }
  35. @Override
  36. protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
  37. return info.getMatchingCondition(request);
  38. }

3. 执行 Handler

RequestMappingHandlerAdapter 是 Spring 中最主要的 HandlerAdapter,它的内部封装了参数解析和结果类型转换的相关组件。我们先看一下 RequestMappingHandlerAdapter 内部默认初始化的组件。

3.1 初始化

(1)属性
RequestMappingHandlerAdapter 中最重要的属性是参数处理器 HandlerMethodArgumentResolver 和结果处理器 HandlerMethodReturnValueHandler。

  1. private HandlerMethodArgumentResolverComposite argumentResolvers; // 1. 参数处理器
  2. private HandlerMethodReturnValueHandlerComposite returnValueHandlers; // 2. 结果处理器
  3. // 3. 以下都是一些重要的辅助工具类
  4. // 3.1. 内容协商。默认从请求头中获取Accept字段:HeaderContentNegotiationStrategy
  5. private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
  6. // 3.2. 媒体类型MediaType格式转换器
  7. private List<HttpMessageConverter<?>> messageConverters;
  8. // 3.3. 解析方法参数名称
  9. private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

(2)afterPropertiesSet

  1. @Override
  2. public void afterPropertiesSet() {
  3. // 1. @ControllerAdvice
  4. initControllerAdviceCache();
  5. // 2. 参数处理器
  6. if (this.argumentResolvers == null) {
  7. List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
  8. this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
  9. }
  10. // 3. @InitBinder 和 @ModelAttribute 处理器
  11. if (this.initBinderArgumentResolvers == null) {
  12. List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
  13. this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
  14. }
  15. // 4. 结果处理器
  16. if (this.returnValueHandlers == null) {
  17. List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
  18. this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
  19. }
  20. }

说明:RequestMappingHandlerAdapter 启动时,初始化了四类组件,分别是:

  1. requestResponseBodyAdvice:(了解)见《Spring-MVC @ControllerAdvice 三种使用场景》。
  2. initBinderArgumentResolvers:(基本不用)见《Spring-MVC @InitBinder 和 @ModelAttribute 使用场景》。
  3. argumentResolvers:(核心)参数处理器,用于解析方法参数。比如,RequestParamMethodArgumentResolver 用于处理 @RequestParam 注解,RequestResponseBodyMethodProcessor 用于处理 @RequestBody 注解。
  4. returnValueHandlers:(核心)结果处理器,用于处理 handler 的返回值。比如,RequestResponseBodyMethodProcessor 就是用于处理 @ResponseBody 注解。

    3.2 执行

    执行的调用链如下:RequestMappingHandlerAdapter#handle -> handleInternal -> invokeHandlerMethod。invokeHandlerMethod 方法配置 ServletInvocableHandlerMethod 的参数处理器和结果处理器,并委托其执行。

    1. protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
    2. HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    3. ServletWebRequest webRequest = new ServletWebRequest(request, response);
    4. try {
    5. // 1. (极少使用)@InitBinder 和 @ModelAttribute处理
    6. WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    7. ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    8. // 2. (核心逻辑)配置invocableMethod的参数处理器、结果处理器、参数名称解析器
    9. ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    10. invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    11. invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    12. invocableMethod.setDataBinderFactory(binderFactory);
    13. invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    14. // 3. (如果是REST风格,只是用来标记请求是否已经处理完成)ModelAndViewContainer
    15. ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    16. mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    17. modelFactory.initModel(webRequest, mavContainer, invocableMethod);
    18. mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
    19. // 4. (基本不用)异步处理
    20. AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
    21. asyncWebRequest.setTimeout(this.asyncRequestTimeout);
    22. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    23. asyncManager.setTaskExecutor(this.taskExecutor);
    24. asyncManager.setAsyncWebRequest(asyncWebRequest);
    25. asyncManager.registerCallableInterceptors(this.callableInterceptors);
    26. asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
    27. if (asyncManager.hasConcurrentResult()) {
    28. Object result = asyncManager.getConcurrentResult();
    29. mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
    30. asyncManager.clearConcurrentResult();
    31. invocableMethod = invocableMethod.wrapConcurrentResult(result);
    32. }
    33. // 5. (核心逻辑)执行任务
    34. invocableMethod.invokeAndHandle(webRequest, mavContainer);
    35. if (asyncManager.isConcurrentHandlingStarted()) {
    36. return null;
    37. }
    38. // 6. (REST风格时返回null)返回ModelAndView
    39. return getModelAndView(mavContainer, modelFactory, webRequest);
    40. } finally {
    41. webRequest.requestCompleted();
    42. }
    43. }

    说明:RequestMappingHandlerAdapter 主要处理异步调用的问题,当业务代码返回的结果是 WebAsyncTask 时,AsyncTaskMethodReturnValueHandler 处理器会开启异步调用。异步调用使用较少,我们先忽略这块。实际上,invokeHandlerMethod 方法的核心非常简单。 ```java ServletInvocableHandlerMethod invocableMethod = new ServletInvocableHandlerMethod(handlerMethod); invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

invocableMethod.invokeAndHandle(webRequest, mavContainer);

  1. **说明:**ServletInvocableHandlerMethod 负责参数解析和结果处理。你可能认为它很复杂,其实也非常简单,参数解析和结果处理分别委托给了 argumentResolvers returnValueHandlers
  2. ```java
  3. public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
  4. Object... providedArgs) throws Exception {
  5. // 1. 执行Handler,包括参数解析
  6. Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
  7. setResponseStatus(webRequest);
  8. if (returnValue == null) {
  9. if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
  10. disableContentCachingIfNecessary(webRequest);
  11. mavContainer.setRequestHandled(true);
  12. return;
  13. }
  14. } else if (StringUtils.hasText(getResponseStatusReason())) {
  15. mavContainer.setRequestHandled(true);
  16. return;
  17. }
  18. mavContainer.setRequestHandled(false);
  19. // 2. 结果处理
  20. this.returnValueHandlers.handleReturnValue(
  21. returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
  22. }
  23. public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
  24. Object... providedArgs) throws Exception {
  25. // 1. 参数解析
  26. Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
  27. // 2. 直接反射调用method.invoke执行
  28. return doInvoke(args);
  29. }

说明:ServletInvocableHandlerMethod 执行时,先委托 argumentResolvers 解析方法的参数,然后调用反射执行handler,最后委托 returnValueHandlers 对返回值的结果进行处理。

4. 参数解析

参数解析时,会调用 RequestMappingHandlerAdapter 配置的 argumentResolvers 参数解析器,逐一解析方法的参数。Spring 默认的参数解析器 HandlerMethodArgumentResolver 有近 20 个,我们会分析其中两个比较典型的参数解析器:

  • RequestParamMethodArgumentResolver:解析 @RequestParam 注解。
  • RequestResponseBodyMethodProcessor:解析 @RequestBody 注解。

    1. // ServletInvocableHandlerMethod
    2. protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
    3. Object... providedArgs) throws Exception {
    4. MethodParameter[] parameters = getMethodParameters();
    5. Object[] args = new Object[parameters.length];
    6. for (int i = 0; i < parameters.length; i++) {
    7. MethodParameter parameter = parameters[i];
    8. // 参数名称解析器parameterNameDiscoverer
    9. // 参数解析器resolvers
    10. parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
    11. if (!this.resolvers.supportsParameter(parameter)) {
    12. throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
    13. }
    14. args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
    15. }
    16. return args;
    17. }

    4.1 @RequestParam

    RequestParamMethodArgumentResolver 首先会判断参数或方法上是否有 @RequestParam 注解。如果有,解析时会调用 request.getParameterValues(name) 获取其 value 值。是不是很简单呢?

    1. protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
    2. HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
    3. ...
    4. Object arg = null;
    5. String[] paramValues = request.getParameterValues(name);
    6. if (paramValues != null) {
    7. arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
    8. }
    9. return arg;
    10. }

    4.2 @RequestBody

    RequestResponseBodyMethodProcessor 同时处理 @RequestBody 和 @ResponseBody 两个注解。本小节先看一下 @RequestBody 是如何处理的。
    RequestResponseBodyMethodProcessor 实现了 HandlerMethodArgumentResolver 接口。参数解析时,根据请求头中的 Content-Type 参数类型进行解析。resolveArgument 解析参数时,最终调用 readWithMessageConverters 方法。

    1. protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
    2. Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
    3. MediaType contentType;
    4. contentType = inputMessage.getHeaders().getContentType();
    5. Class<?> contextClass = parameter.getContainingClass();
    6. Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
    7. if (targetClass == null) {
    8. ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
    9. targetClass = (Class<T>) resolvableType.resolve();
    10. }
    11. Object body = NO_VALUE;
    12. EmptyBodyCheckingHttpInputMessage message;
    13. try {
    14. message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
    15. for (HttpMessageConverter<?> converter : this.messageConverters) {
    16. Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
    17. GenericHttpMessageConverter<?> genericConverter =
    18. (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
    19. // HttpMessageConverter根据媒体类型来解析参数
    20. if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
    21. (targetClass != null && converter.canRead(targetClass, contentType))) {
    22. if (message.hasBody()) {
    23. HttpInputMessage msgToUse =
    24. getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
    25. // (核心代码)参数解析
    26. body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
    27. ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
    28. body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
    29. } else {
    30. body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
    31. }
    32. break;
    33. }
    34. }
    35. }
    36. return body;
    37. }

    说明:readWithMessageConverters 方法先获取请求头中的 Content-Type 参数类型。最终调用具体的 HttpMessageConverter 来解析该媒体类型。

    5. 结果处理

    Spring 默认有多个结果处理器 HandlerMethodReturnValueHandler,在这里我们只关注 REST 注解 @ResponseBody 的处理器 RequestResponseBodyMethodProcessor。

    5.1 @ResponseBody

    本小节继续分析 RequestResponseBodyMethodProcessor 对 @ResponseBody 注解的处理。RequestResponseBodyMethodProcessor 同时实现了 HandlerMethodReturnValueHandler 接口。调用链为 handleReturnValue -> writeWithMessageConverters。处理的过程如下:

  1. 解析方法返回值类型。
  2. 获取响应的媒体类型 MediaType。如果指定了响应的 Content-Type,则直接获取。未指定,则获取请求的 Accept 值和服务端可以生成的类型,取一个优先级最高的类型。
  3. 根据媒体类型选择合适的转换器。选择支持该 MediaType 的 HttpMessageConverter,转换成对应的格式,并发送 restful。

    1. protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
    2. ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
    3. throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    4. Object body;
    5. Class<?> valueType;
    6. Type targetType;
    7. // 1. 解析返回值类型
    8. if (value instanceof CharSequence) {
    9. body = value.toString();
    10. valueType = String.class;
    11. targetType = String.class;
    12. } else {
    13. body = value;
    14. valueType = getReturnValueType(body, returnType);
    15. targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
    16. }
    17. ...
    18. // 2. 解析Http响应的媒体类型
    19. MediaType selectedMediaType = null;
    20. MediaType contentType = outputMessage.getHeaders().getContentType();
    21. if (contentType != null && contentType.isConcrete()) {
    22. selectedMediaType = contentType;
    23. } else {
    24. HttpServletRequest request = inputMessage.getServletRequest();
    25. List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
    26. List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
    27. if (body != null && producibleTypes.isEmpty()) {
    28. throw new HttpMessageNotWritableException(
    29. "No converter found for return value of type: " + valueType);
    30. }
    31. List<MediaType> mediaTypesToUse = new ArrayList<>();
    32. for (MediaType requestedType : acceptableTypes) {
    33. for (MediaType producibleType : producibleTypes) {
    34. if (requestedType.isCompatibleWith(producibleType)) {
    35. mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
    36. }
    37. }
    38. }
    39. if (mediaTypesToUse.isEmpty()) {
    40. if (body != null) {
    41. throw new HttpMediaTypeNotAcceptableException(producibleTypes);
    42. }
    43. return;
    44. }
    45. MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
    46. for (MediaType mediaType : mediaTypesToUse) {
    47. if (mediaType.isConcrete()) {
    48. selectedMediaType = mediaType;
    49. break;
    50. } else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
    51. selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
    52. break;
    53. }
    54. }
    55. }
    56. // 3. 根据媒体类型MediaType响应请求
    57. if (selectedMediaType != null) {
    58. selectedMediaType = selectedMediaType.removeQualityValue();
    59. for (HttpMessageConverter<?> converter : this.messageConverters) {
    60. GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
    61. (GenericHttpMessageConverter<?>) converter : null);
    62. if (genericConverter != null ?
    63. ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
    64. converter.canWrite(valueType, selectedMediaType)) {
    65. body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
    66. (Class<? extends HttpMessageConverter<?>>) converter.getClass(),
    67. inputMessage, outputMessage);
    68. if (body != null) {
    69. Object theBody = body;
    70. addContentDispositionHeader(inputMessage, outputMessage);
    71. // 4. (核心代码)类型转换
    72. if (genericConverter != null) {
    73. genericConverter.write(body, targetType, selectedMediaType, outputMessage);
    74. } else {
    75. ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
    76. }
    77. }
    78. return;
    79. }
    80. }
    81. }
    82. if (body != null) {
    83. throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
    84. }
    85. }

    说明:writeWithMessageConverters 方法根据请求的 Accept 类型,选择合适的 HttpMessageConverter 处理数据。可以看到,SpringMVC 中使用 HttpMessageConverter 进行媒体类型转换。

    6. 总结时刻

    推荐阅读


每天用心记录一点点。内容也许不重要,但习惯很重要!