Springboot转换器

源码分析

加载

​ 在WebMvcConfigurationSupport中初始化RequestMappingHandlerAdapter

@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
   RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
   adapter.setContentNegotiationManager(mvcContentNegotiationManager());
   adapter.setMessageConverters(getMessageConverters());
   adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
   adapter.setCustomArgumentResolvers(getArgumentResolvers());
   adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

   if (jackson2Present) {
      adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
      adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
   }

   AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
   configureAsyncSupport(configurer);
   if (configurer.getTaskExecutor() != null) {
      adapter.setTaskExecutor(configurer.getTaskExecutor());
   }
   if (configurer.getTimeout() != null) {
      adapter.setAsyncRequestTimeout(configurer.getTimeout());
   }
   adapter.setCallableInterceptors(configurer.getCallableInterceptors());
   adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

   return adapter;
}

​ 其中 adapter.setMessageConverters(getMessageConverters())设置了转换器,查看getMessageConverters()方法

protected final List> getMessageConverters() {
   if (this.messageConverters == null) {
      this.messageConverters = new ArrayList<>();
      configureMessageConverters(this.messageConverters);
      if (this.messageConverters.isEmpty()) {
         addDefaultHttpMessageConverters(this.messageConverters);
      }
      extendMessageConverters(this.messageConverters);
   }
   return this.messageConverters;
}

​ 通过以上代码不难发现,我们可以通过继承WebMvcConfigurationSupport重写configureMessageConverters()方法,实现加载自定义的类型转换器。默认情况下会执行addDefaultHttpMessageConverters(this.messageConverters),后面的extendMessageConverters()方法也可以重写以实现在注册默认转换器后加载自定义转换器。进入addDefaultHttpMessageConverters

protected final void addDefaultHttpMessageConverters(List> messageConverters) {
   StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
   stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316

   messageConverters.add(new ByteArrayHttpMessageConverter());
   messageConverters.add(stringHttpMessageConverter);
   messageConverters.add(new ResourceHttpMessageConverter());
   messageConverters.add(new ResourceRegionHttpMessageConverter());
   try {
      messageConverters.add(new SourceHttpMessageConverter<>());
   }
   catch (Throwable ex) {
      // Ignore when no TransformerFactory implementation is available...
   }
   messageConverters.add(new AllEncompassingFormHttpMessageConverter());

   if (romePresent) {
      messageConverters.add(new AtomFeedHttpMessageConverter());
      messageConverters.add(new RssChannelHttpMessageConverter());
   }

   if (jackson2XmlPresent) {
      Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
      if (this.applicationContext != null) {
         builder.applicationContext(this.applicationContext);
      }
      messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
   }
   else if (jaxb2Present) {
      messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
   }

   if (jackson2Present) {
      Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
      if (this.applicationContext != null) {
         builder.applicationContext(this.applicationContext);
      }
      messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
   }
   else if (gsonPresent) {
      messageConverters.add(new GsonHttpMessageConverter());
   }
   else if (jsonbPresent) {
      messageConverters.add(new JsonbHttpMessageConverter());
   }

   if (jackson2SmilePresent) {
      Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.smile();
      if (this.applicationContext != null) {
         builder.applicationContext(this.applicationContext);
      }
      messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build()));
   }
   if (jackson2CborPresent) {
      Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.cbor();
      if (this.applicationContext != null) {
         builder.applicationContext(this.applicationContext);
      }
      messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build()));
   }
}

​ 以XML转换器的加载为例,上面代码判断了jackson2XmlPresent标志,该标志在静态代码块中赋值

static {
   ClassLoader classLoader = WebMvcConfigurationSupport.class.getClassLoader();
   romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
   jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
   jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
               ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
   jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
   jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
   jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
   gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
   jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
}

​ 可以看到,当com.fasterxml.jackson.dataformat.xml.XmlMapper类存在时,就会加载MappingJackson2XmlHttpMessageConverter此XML转换器,否则就使用Jaxb解析器。

转换

RequestResponseBodyMethodProcessor类用来处理@RequestBody@ResponseBody标记的请求参数和返回值。

请求参数

​ 请求进来时会进入RequestResponseBodyMethodProcessor以下代码:

protected  Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
      Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

   HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
   Assert.state(servletRequest != null, "No HttpServletRequest");
   ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);

   Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
   if (arg == null && checkRequired(parameter)) {
      throw new HttpMessageNotReadableException("Required request body is missing: " +
            parameter.getExecutable().toGenericString(), inputMessage);
   }
   return arg;
}

​ 父类AbstractMessageConverterMethodArgumentResolver实现具体的转换类处理:

protected  Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
      Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

   MediaType contentType;
   boolean noContentType = false;
   try {
      contentType = inputMessage.getHeaders().getContentType();
   }
   catch (InvalidMediaTypeException ex) {
      throw new HttpMediaTypeNotSupportedException(ex.getMessage());
   }
   if (contentType == null) {
      noContentType = true;
      contentType = MediaType.APPLICATION_OCTET_STREAM;
   }

   Class contextClass = parameter.getContainingClass();
   Class targetClass = (targetType instanceof Class ? (Class) targetType : null);
   if (targetClass == null) {
      ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
      targetClass = (Class) resolvableType.resolve();
   }

   HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
   Object body = NO_VALUE;

   EmptyBodyCheckingHttpInputMessage message;
   try {
      message = new EmptyBodyCheckingHttpInputMessage(inputMessage);

      for (HttpMessageConverter converter : this.messageConverters) {
         Class> converterType = (Class>) converter.getClass();
         GenericHttpMessageConverter genericConverter =
               (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter) converter : null);
         if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
               (targetClass != null && converter.canRead(targetClass, contentType))) {
            if (message.hasBody()) {
               HttpInputMessage msgToUse =
                     getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
               body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                     ((HttpMessageConverter) converter).read(targetClass, msgToUse));
               body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
            }
            else {
               body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
            }
            break;
         }
      }
   }
   catch (IOException ex) {
      throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
   }

   if (body == NO_VALUE) {
      if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
            (noContentType && !message.hasBody())) {
         return null;
      }
      throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
   }

   MediaType selectedContentType = contentType;
   Object theBody = body;
   LogFormatUtils.traceDebug(logger, traceOn -> {
      String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
      return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
   });

   return body;
}

​ 以上代码根据http请求过来的媒体类型选择具体的转换器(如application/jsonapplication/*+json会被MappingJackson2HttpMessageConverter转换)。

返回值

​ 返回值经过以下RequestResponseBodyMethodProcessor代码处理:

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

writeWithMessageConverters()方法核心逻辑:

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

​ 以上代码根据返回的Http媒体类型选择具体的转换器(如application/xmlapplication/*+xmltext/xml会被MappingJackson2XmlHttpMessageConverter转换)。

你可能感兴趣的:(Springboot转换器)