• spring学习笔记(七)HttpMessageConverter


    1. HttpMessageConverter的加载

    • 首先我们找到WebMvcAutoConfiguration这个类

      在其中我们可以看到这么一段代码

      @Configuration
      @Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
      @EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})
      @Order(0)    //表示最先加载
      public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware {
              private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);
              private final ResourceProperties resourceProperties;
              private final WebMvcProperties mvcProperties;
              private final ListableBeanFactory beanFactory;
              private final ObjectProvider<HttpMessageConverters> messageConvertersProvider;
              final WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
              private ResourceLoader resourceLoader;
      
              public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
                  this.resourceProperties = resourceProperties;
                  this.mvcProperties = mvcProperties;
                  this.beanFactory = beanFactory;
                  this.messageConvertersProvider = messageConvertersProvider;
                  this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
              }
      
              public void setResourceLoader(ResourceLoader resourceLoader) {
                  this.resourceLoader = resourceLoader;
              }
      
              public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {          this.messageConvertersProvider.ifAvailable((customConverters) -> {
                  // 添加自定义的converter
                      converters.addAll(customConverters.getConverters());
                  });
              }
           // 省略一部分不重要的代码
      	.....
      
    • 上面我们要分析的核心代码就是configureMessageConverters(List<HttpMessageConverter<?>> converters)这个方法,首先我要知道这个方法的入参是什么,在哪里初始化的。

    • 我们可以看到在这个类上有一个@import注解

      @Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})

    • 我们可以跟一下这个类----》EnableWebMvcConfiguration

    • 我们可以发现这个类继承了WebMvcConfigurationSupport,核心的关于messageConverter的代码如下:

      protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
              StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
              stringHttpMessageConverter.setWriteAcceptCharset(false);
              messageConverters.add(new ByteArrayHttpMessageConverter());
              messageConverters.add(stringHttpMessageConverter);
              messageConverters.add(new ResourceHttpMessageConverter());
              messageConverters.add(new ResourceRegionHttpMessageConverter());
      
              try {
                  messageConverters.add(new SourceHttpMessageConverter());
              } catch (Throwable var4) {
              }
      
              messageConverters.add(new AllEncompassingFormHttpMessageConverter());
              if (romePresent) {
                  messageConverters.add(new AtomFeedHttpMessageConverter());
                  messageConverters.add(new RssChannelHttpMessageConverter());
              }
      
              Jackson2ObjectMapperBuilder builder;
              if (jackson2XmlPresent) {
                  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) {
                  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) {
                  builder = Jackson2ObjectMapperBuilder.smile();
                  if (this.applicationContext != null) {
                      builder.applicationContext(this.applicationContext);
                  }
      
                  messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build()));
              }
      
              if (jackson2CborPresent) {
                  builder = Jackson2ObjectMapperBuilder.cbor();
                  if (this.applicationContext != null) {
                      builder.applicationContext(this.applicationContext);
                  }
      
                  messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build()));
              }
      
          }
      
    • 从这段代码中我们可以知道

      1. spring中默认添加了几个消息转换器,比如ByteArrayHttpMessageConverter,stringHttpMessageConverter,ResourceHttpMessageConverter等等
      2. 会根据Jackson,Gson等添加对应的小心转换器

    2. 从StringMessageConverter探究消息转换器的原理

    public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {
        // 默认字符集
        public static final Charset DEFAULT_CHARSET;
        @Nullable
        private volatile List<Charset> availableCharsets;
        private boolean writeAcceptCharset;
    
        public StringHttpMessageConverter() {
            this(DEFAULT_CHARSET);
        }
    
        public StringHttpMessageConverter(Charset defaultCharset) {
            super(defaultCharset, new MediaType[]{MediaType.TEXT_PLAIN, MediaType.ALL});
            this.writeAcceptCharset = true;
        }
    
        public void setWriteAcceptCharset(boolean writeAcceptCharset) {
            this.writeAcceptCharset = writeAcceptCharset;
        }
    
        public boolean supports(Class<?> clazz) {
            return String.class == clazz;
        }
    
        protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
            Charset charset = this.getContentTypeCharset(inputMessage.getHeaders().getContentType());
            return StreamUtils.copyToString(inputMessage.getBody(), charset);
        }
    
        protected Long getContentLength(String str, @Nullable MediaType contentType) {
            Charset charset = this.getContentTypeCharset(contentType);
            return (long)str.getBytes(charset).length;
        }
    
        protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
            if (this.writeAcceptCharset) {
                outputMessage.getHeaders().setAcceptCharset(this.getAcceptedCharsets());
            }
    
            Charset charset = this.getContentTypeCharset(outputMessage.getHeaders().getContentType());
            StreamUtils.copy(str, charset, outputMessage.getBody());
        }
    
        protected List<Charset> getAcceptedCharsets() {
            List<Charset> charsets = this.availableCharsets;
            if (charsets == null) {
                charsets = new ArrayList(Charset.availableCharsets().values());
                this.availableCharsets = (List)charsets;
            }
    
            return (List)charsets;
        }
    
        private Charset getContentTypeCharset(@Nullable MediaType contentType) {
            if (contentType != null && contentType.getCharset() != null) {
                return contentType.getCharset();
            } else if (contentType != null && contentType.isCompatibleWith(MediaType.APPLICATION_JSON)) {
                return StandardCharsets.UTF_8;
            } else {
                Charset charset = this.getDefaultCharset();
                Assert.state(charset != null, "No default charset");
                return charset;
            }
        }
    
        static {
            // 默认字符集为ISO-8859-1
            DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;
        }
    }
    

    ​ 上面代码的核心就在两个读写的方法上,分别是将字符串转换为httpMessage,跟将httpMessage转换为字符串

    // 读方法
    protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
            Charset charset = 
       // 根据请求头中的信息,得到编码类型并转换         
       this.getContentTypeCharset(inputMessage.getHeaders().getContentType());
            return StreamUtils.copyToString(inputMessage.getBody(), charset);
    }
    
    // 写方法
    protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
            if (this.writeAcceptCharset) {
                outputMessage.getHeaders().setAcceptCharset(this.getAcceptedCharsets());
            }
    
            Charset charset = 
             // 根据响应头中的编码类型,将字符串转换为httpMessage消息  
                this.getContentTypeCharset(outputMessage.getHeaders().getContentType());
            StreamUtils.copy(str, charset, outputMessage.getBody());
        }
    
    
  • 相关阅读:
    【Xamarin开发 Android 系列 6】 Android 结构基础(上)
    SQL语句,标准表达式中数据类型不匹配
    VS工程目录下的ipch文件夹和.sdf文件
    VS2013和VS2010工具集和字符集
    SQL语句:语法错误(操作符丢失)在查询表达式中
    数据库改动后,发现第二张图片不能存储并显示
    BEGIN_MESSAGE_MAP(Caccess_test_1Dlg, CDialogEx)
    error C2678: 二进制“+”: 没有找到接受“const char [22]”类型的左操作数的运算符(或没有可接受的转换)没有与这些操作数匹配的“+”运算符
    MFC中给控件添加变量,DoDataExchange中
    数据库添加表时设置表名为中文
  • 原文地址:https://www.cnblogs.com/daimzh/p/12854470.html
Copyright © 2020-2023  润新知