zoukankan      html  css  js  c++  java
  • 源码分析springboot自定义jackson序列化,默认null值个性化处理返回值

      最近项目要实现一种需求,对于后端返回给前端的json格式的一种规范,不允许缺少字段和字段值都为null,所以琢磨了一下如何进行将springboot的Jackson序列化自定义一下,先看看如何实现,再去看源码

    第一步:写配置类

     1 @Configuration
     2 public class WebConfiguration  extends WebMvcConfigurationSupport {
     3 @Override
     4     protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
     5         converters.stream().filter(c -> c instanceof MappingJackson2HttpMessageConverter)
     6                 .map(c ->(MappingJackson2HttpMessageConverter)c)
     7                 .forEach(c->{
     8                     ObjectMapper mapper = c.getObjectMapper();
     9                     // 为mapper注册一个带有SerializerModifier的Factory,此modifier主要做的事情为:当序列化类型为array,list、set时,当值为空时,序列化成[]
    10                     mapper.setSerializerFactory(mapper.getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier()));
    11                     c.setObjectMapper(mapper);
    12                 });
    13     }
    14 }

    第二步:编写值为null时的自定义序列化

      1 /**
      2  * @title: MyBeanSerializerModifier
      3  * @Author junyu
      4  * 旧巷里有一个穿着白衬衫笑起来如太阳般温暖我的少年。
      5  * 记忆里有一个穿着连衣裙哭起来如孩子般讨人喜的女孩。
      6  * 他说,哪年树弯了腰,人见了老,桃花落了白发梢,他讲的笑话她还会笑,那便是好。
      7  * 她说,哪年国改了号,坟长了草,地府过了奈何桥,她回头看时他还在瞧,就不算糟。
      8  * @Date: 2020/9/12 16:44
      9  * @Version 1.0
     10  */
     11 public class MyBeanSerializerModifier extends BeanSerializerModifier {
     12 
     13     private MyNullStringJsonSerializer myNullStringJsonSerializer;
     14     private MyNullArrayJsonSerializer MyNullArrayJsonSerializer;
     15     private MyNullObjectJsonSerializer MyNullObjectJsonSerializer;
     16     private MyNullJsonSerializer myNullJsonSerializer;
     17 
     18     public MyBeanSerializerModifier(){
     19         myNullStringJsonSerializer = new MyNullStringJsonSerializer();
     20         MyNullArrayJsonSerializer = new MyNullArrayJsonSerializer();
     21         MyNullObjectJsonSerializer =  new MyNullObjectJsonSerializer();
     22         myNullJsonSerializer = new MyNullJsonSerializer();
     23     }
     24 
     25     @Override
     26     public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
     27                                                      List<BeanPropertyWriter> beanProperties) {
     28         // 循环所有的beanPropertyWriter
     29         beanProperties.forEach(writer ->{
     30             // 判断字段的类型
     31             if (isArrayType(writer)) {
     32                 //给writer注册一个自己的nullSerializer
     33                 writer.assignNullSerializer(MyNullArrayJsonSerializer);
     34             } else if (isObjectType(writer)) {
     35                 writer.assignNullSerializer(MyNullObjectJsonSerializer);
     36             } else if (isStringType(writer)) {
     37                 writer.assignNullSerializer(myNullStringJsonSerializer);
     38             } else if (isPrimitiveType(writer)) {
     39                 writer.assignNullSerializer(myNullJsonSerializer);
     40             }
     41         });
     42         return beanProperties;
     43     }
     44 
     45     // 判断是否是boolean类型
     46     private boolean isPrimitiveType(BeanPropertyWriter writer) {
     47         Class<?> clazz = writer.getType().getRawClass();
     48         return clazz.isPrimitive();
     49     }
     50 
     51     // 判断是否是string类型
     52     private boolean isStringType(BeanPropertyWriter writer) {
     53         Class<?> clazz = writer.getType().getRawClass();
     54         return clazz.equals(String.class);
     55     }
     56 
     57     // 判断是否是对象类型
     58     private boolean isObjectType(BeanPropertyWriter writer) {
     59         Class<?> clazz = writer.getType().getRawClass();
     60         return !clazz.isPrimitive() && !clazz.equals(String.class)
     61                 && clazz.isAssignableFrom(Object.class);
     62     }
     63     // 判断是否是集合类型
     64     protected boolean isArrayType(BeanPropertyWriter writer) {
     65         Class<?> clazz = writer.getType().getRawClass();
     66         return clazz.isArray() || clazz.equals(List.class) || clazz.equals(Set.class);
     67     }
     68 
     69     class MyNullJsonSerializer extends JsonSerializer<Object>{
     70 
     71         @Override
     72         public void serialize(Object value, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
     73             if (value == null) {
     74                 jgen.writeNull();
     75             }
     76         }
     77     }
     78 
     79 
     80     class MyNullStringJsonSerializer extends JsonSerializer<Object>{
     81 
     82         @Override
     83         public void serialize(Object value, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
     84             if (value == null) {
     85                 jgen.writeString(StringUtils.EMPTY);
     86             }
     87         }
     88     }
     89 
     90     class MyNullArrayJsonSerializer extends JsonSerializer<Object>{
     91 
     92         @Override
     93         public void serialize(Object value, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
     94             if (value == null) {
     95                 jgen.writeStartArray();
     96                 jgen.writeEndArray();
     97             }
     98         }
     99     }
    100 
    101     class MyNullObjectJsonSerializer extends JsonSerializer<Object>{
    102 
    103         @Override
    104         public void serialize(Object value, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
    105             if (value == null) {
    106                 jgen.writeStartObject();
    107                 jgen.writeEndObject();
    108             }
    109         }
    110     }
    111 
    112 }

      这样基本配置就完事了,现在可以试试效果了,自己定义一个bean用来返回,定义一个简单的controller去接受访问就行了,博主就不进行写这两个类了。返回结果如下

       这是我的项目需求需要实现的,大家可以根据的自己的需求去改写MyBeanSerializerModifier这个类。还有另一种实现方式:不继承

     1 @Configuration
     2 public class WebConfiguration {
     3 
     4 @Bean
     5     public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){
     6         MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
     7         ObjectMapper mapper = mappingJackson2HttpMessageConverter.getObjectMapper();
     8         mapper.setSerializerFactory(mapper.getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier()));
     9         mappingJackson2HttpMessageConverter.setObjectMapper(mapper);
    10         return mappingJackson2HttpMessageConverter;
    11     }
    12 
    13 }

      这种方法也是可以设置成功的,主要是不是继承了WebMvcConfigurationSupport类,毕竟这个类有很多可以自定义的方法,用起来顺手而已。

      第一个问题:为什么继承WebMvcConfigurationSupport后,要重写extendMessageConverters方法;

      第二个问题:为什么继承WebMvcConfigurationSupport后,再去生成@Bean的MappingJackson2HttpMessageConverter,却不生效;

      第三个问题:为什么不继承WebMvcConfigurationSupport时,生成@Bean的MappingJackson2HttpMessageConverter是生效的;

      这几个问题,都需要我们进入源码观察,废活不多说,我们来进入源码的世界。解决问题之前必须搞清楚在哪里进行了序列化。

      第一步:我们要弄清楚在哪里进行的Jackson序列化,看这里https://www.processon.com/embed/5f5c6464f346fb7afd55448b,从返回请求开始的序列化基本流程就在这里了,虽然图有点low,但是清楚的记录的每一步,我们主要看一下下面的源码

     1 /*
     2     /**********************************************************
     3     /* Field serialization methods
     4     /**********************************************************
     5      */
     6     //序列化每一个字段
     7     protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider)
     8             throws IOException
     9     {
    10         final BeanPropertyWriter[] props;
    11         if (_filteredProps != null && provider.getActiveView() != null) {
    12             props = _filteredProps;
    13         } else {
    14             props = _props;
    15         }
    16         int i = 0;
    17         try {
    18             for (final int len = props.length; i < len; ++i) {
    19                 BeanPropertyWriter prop = props[i];
    20                 if (prop != null) { // can have nulls in filtered list
    21                     //关键就在这一步进行的序列化,而为什么BeanPropertyWriter是数组,我们一会解释
    22                     prop.serializeAsField(bean, gen, provider);
    23                 }
    24             }
    25             if (_anyGetterWriter != null) {
    26                 _anyGetterWriter.getAndSerialize(bean, gen, provider);
    27             }
    28         } catch (Exception e) {
    29             String name = (i == props.length) ? "[anySetter]" : props[i].getName();
    30             wrapAndThrow(provider, e, bean, name);
    31         } catch (StackOverflowError e) {
    32             // 04-Sep-2009, tatu: Dealing with this is tricky, since we don't have many
    33             //   stack frames to spare... just one or two; can't make many calls.
    34 
    35             // 10-Dec-2015, tatu: and due to above, avoid "from" method, call ctor directly:
    36             //JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e);
    37             JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e);
    38 
    39             String name = (i == props.length) ? "[anySetter]" : props[i].getName();
    40             mapE.prependPath(new JsonMappingException.Reference(bean, name));
    41             throw mapE;
    42         }
    43     }

      既然已经找到了在哪里要进行序列化,那我们看看是如何实现的:

     1 /**
     2      * Method called to access property that this bean stands for, from within
     3      * given bean, and to serialize it as a JSON Object field using appropriate
     4      * serializer.
     5      */
     6     @Override
     7     public void serializeAsField(Object bean, JsonGenerator gen,
     8                                  SerializerProvider prov) throws Exception {
     9         // inlined 'get()'
    10         final Object value = (_accessorMethod == null) ? _field.get(bean)
    11                 : _accessorMethod.invoke(bean, (Object[]) null);
    12 
    13         // Null handling is bit different, check that first
    14         if (value == null) {
    15             //看到这里大家应该就知道null值是如何进行序列化 的了,如果不配置的话,默认是返回null
    16             //因为_nullSerializer是有默认值的,大家看一看这个类的初始化
    17             //那我们要是改一下_nullSerializer的这个默认类,让每一个字段调用我们自己的_nullSerializer不就可以了吗,
    18             //yes、我们就这么干
    19             if (_nullSerializer != null) {
    20                 gen.writeFieldName(_name);
    21                 _nullSerializer.serialize(null, gen, prov);
    22             }
    23             return;
    24         }
    25         // then find serializer to use
    26         JsonSerializer<Object> ser = _serializer;
    27         if (ser == null) {
    28             Class<?> cls = value.getClass();
    29             PropertySerializerMap m = _dynamicSerializers;
    30             ser = m.serializerFor(cls);
    31             if (ser == null) {
    32                 ser = _findAndAddDynamic(m, cls, prov);
    33             }
    34         }
    35         // and then see if we must suppress certain values (default, empty)
    36         if (_suppressableValue != null) {
    37             if (MARKER_FOR_EMPTY == _suppressableValue) {
    38                 if (ser.isEmpty(prov, value)) {
    39                     return;
    40                 }
    41             } else if (_suppressableValue.equals(value)) {
    42                 return;
    43             }
    44         }
    45         // For non-nulls: simple check for direct cycles
    46         if (value == bean) {
    47             // three choices: exception; handled by call; or pass-through
    48             if (_handleSelfReference(bean, gen, prov, ser)) {
    49                 return;
    50             }
    51         }
    52         gen.writeFieldName(_name);
    53         if (_typeSerializer == null) {
    54             ser.serialize(value, gen, prov);
    55         } else {
    56             ser.serializeWithType(value, gen, prov, _typeSerializer);
    57         }
    58     }

      那我们来解决第一个问题:为什么继承WebMvcConfigurationSupport后,要重写extendMessageConverters方法?

      不知道大家记得不记得我们请求过来的时候,如果我们配置类集成了WebMvcConfigurationSupport类,dispatchservlet处理handle请求的ha,其实就是RequestMappingHandlerAdapter类,这个类是在WebMvcConfigurationSupport配置的,看源码:

     1 @Bean
     2     public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
     3             @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
     4             @Qualifier("mvcConversionService") FormattingConversionService conversionService,
     5             @Qualifier("mvcValidator") Validator validator) {
     6 
     7         RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
     8         adapter.setContentNegotiationManager(contentNegotiationManager);
     9         adapter.setMessageConverters(getMessageConverters());
    10         adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
    11         adapter.setCustomArgumentResolvers(getArgumentResolvers());
    12         adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
    13 
    14         if (jackson2Present) {
    15             adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
    16             adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
    17         }
    18 
    19         AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
    20         configureAsyncSupport(configurer);
    21         if (configurer.getTaskExecutor() != null) {
    22             adapter.setTaskExecutor(configurer.getTaskExecutor());
    23         }
    24         if (configurer.getTimeout() != null) {
    25             adapter.setAsyncRequestTimeout(configurer.getTimeout());
    26         }
    27         adapter.setCallableInterceptors(configurer.getCallableInterceptors());
    28         adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());
    29 
    30         return adapter;
    31     }

      adapter.setMessageConverters(getMessageConverters());当大家看到这个方法的时候,应该就会想到我们的默认jackson转换器:MappingJackson2HttpMessageConverter,我们看看这个getMessageConverters()有什么幺蛾子:

     1 protected final List<HttpMessageConverter<?>> getMessageConverters() {
     2         if (this.messageConverters == null) {
     3             this.messageConverters = new ArrayList<>();
     4             configureMessageConverters(this.messageConverters);
     5             if (this.messageConverters.isEmpty()) {
     6                 addDefaultHttpMessageConverters(this.messageConverters);
     7             }
     8             extendMessageConverters(this.messageConverters);
     9         }
    10         return this.messageConverters;
    11     } 
     1 protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
     2 
     3             //这些都不用管,跟我们的需求没啥作用,我们只看关键的部分,在下面
     4             messageConverters.add(new ByteArrayHttpMessageConverter());
     5             messageConverters.add(new StringHttpMessageConverter());
     6             messageConverters.add(new ResourceHttpMessageConverter());
     7             messageConverters.add(new ResourceRegionHttpMessageConverter());
     8             try {
     9                 messageConverters.add(new SourceHttpMessageConverter<>());
    10             }
    11             catch (Throwable ex) {
    12                 // Ignore when no TransformerFactory implementation is available...
    13             }
    14             messageConverters.add(new AllEncompassingFormHttpMessageConverter());
    15 
    16             if (romePresent) {
    17                 messageConverters.add(new AtomFeedHttpMessageConverter());
    18                 messageConverters.add(new RssChannelHttpMessageConverter());
    19             }
    20 
    21             if (jackson2XmlPresent) {
    22                 Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
    23                 if (this.applicationContext != null) {
    24                     builder.applicationContext(this.applicationContext);
    25                 }
    26                 messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
    27             }
    28             else if (jaxb2Present) {
    29                 messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
    30             }
    31 
    32             if (jackson2Present) {
    33                 Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
    34                 if (this.applicationContext != null) {
    35                     builder.applicationContext(this.applicationContext);
    36                 }
    37                 //解析我们返回值的转换器就是在这里生成的
    38                 messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
    39             }
    40             else if (gsonPresent) {
    41                 messageConverters.add(new GsonHttpMessageConverter());
    42             }
    43             else if (jsonbPresent) {
    44                 messageConverters.add(new JsonbHttpMessageConverter());
    45             }
    46 
    47             if (jackson2SmilePresent) {
    48                 Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.smile();
    49                 if (this.applicationContext != null) {
    50                     builder.applicationContext(this.applicationContext);
    51                 }
    52                 messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build()));
    53             }
    54             if (jackson2CborPresent) {
    55                 Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.cbor();
    56                 if (this.applicationContext != null) {
    57                     builder.applicationContext(this.applicationContext);
    58                 }
    59                 messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build()));
    60             }
    61         }
    addDefaultHttpMessageConverters

      我们的MappingJackson2HttpMessageConverter类就是这里初始化的,初始化的时候默认的_nullSerializer也会被初始化,大家肯定说这已经初始化完了,该咋办,大家应该看到了extendMessageConverters(this.messageConverters);这个方法就是用来重写实现的了,这回知道我们继承WebMvcConfigurationSupport后,为什么要重写extendMessageConverters,我们的配置类遍历已经获取到的convert,然后对我们想要的转换器进行修改添加,那修改完了,是在哪里起作用的呢,我们再来看一看源码:

    在序列化之前有一些方法是可以进行修改操作的,在调用writeWithMessageConverters方法的时候:

     1 protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
     2                                                       ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
     3                 throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
     4 
     5             .......
     6 
     7             MediaType selectedMediaType = null;
     8             MediaType contentType = outputMessage.getHeaders().getContentType();
     9             boolean isContentTypePreset = contentType != null && contentType.isConcrete();
    10             if (isContentTypePreset) {
    11                 if (logger.isDebugEnabled()) {
    12                     logger.debug("Found 'Content-Type:" + contentType + "' in response");
    13                 }
    14                 selectedMediaType = contentType;
    15             }
    16             else {
    17                 HttpServletRequest request = inputMessage.getServletRequest();
    18                 List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
    19                 //这里进行自定义操作修改MappingJackson2HttpMessageConverter
    20                 List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
    21 
    22                 .......
    23 
    24             if (selectedMediaType != null) {
    25                 selectedMediaType = selectedMediaType.removeQualityValue();
    26                 //这这里进行选择我们的MappingJackson2HttpMessageConverter去自定义序列化
    27                 for (HttpMessageConverter<?> converter : this.messageConverters) {
    28                     GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
    29                             (GenericHttpMessageConverter<?>) converter : null);
    30                     if (genericConverter != null ?
    31                             ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
    32                             converter.canWrite(valueType, selectedMediaType)) {
    33                         body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
    34                                 (Class<? extends HttpMessageConverter<?>>) converter.getClass(),
    35                                 inputMessage, outputMessage);
    36                         if (body != null) {
    37                             Object theBody = body;
    38                             LogFormatUtils.traceDebug(logger, traceOn ->
    39                                     "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
    40                             addContentDispositionHeader(inputMessage, outputMessage);
    41                             if (genericConverter != null) {
    42                                 genericConverter.write(body, targetType, selectedMediaType, outputMessage);
    43                             }
    44                             else {
    45                                 ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
    46                             }
    47                         }
    48                         else {
    49                             if (logger.isDebugEnabled()) {
    50                                 logger.debug("Nothing to write: null body");
    51                             }
    52                         }
    53                         return;
    54                     }
    55                 }
    56             }
    57 
    58             if (body != null) {
    59                 Set<MediaType> producibleMediaTypes =
    60                         (Set<MediaType>) inputMessage.getServletRequest()
    61                                 .getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
    62 
    63                 if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
    64                     throw new HttpMessageNotWritableException(
    65                             "No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
    66                 }
    67                 throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
    68             }
    69         }
    writeWithMessageConverters

      我们一直追踪getProducibleMediaTypes后,最终发现会调用BeanSerializerFactory的constructBeanOrAddOnSerializer,就是这里进行修改操作的。

     1 protected JsonSerializer<Object> constructBeanOrAddOnSerializer(SerializerProvider prov,
     2             JavaType type, BeanDescription beanDesc, boolean staticTyping)
     3         throws JsonMappingException
     4     {
     5         // 13-Oct-2010, tatu: quick sanity check: never try to create bean serializer for plain Object
     6         // 05-Jul-2012, tatu: ... but we should be able to just return "unknown type" serializer, right?
     7         if (beanDesc.getBeanClass() == Object.class) {
     8             return prov.getUnknownTypeSerializer(Object.class);
     9 //            throw new IllegalArgumentException("Cannot create bean serializer for Object.class");
    10         }
    11         final SerializationConfig config = prov.getConfig();
    12         BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc);
    13         builder.setConfig(config);
    14 
    15         // First: any detectable (auto-detect, annotations) properties to serialize?
    16         List<BeanPropertyWriter> props = findBeanProperties(prov, beanDesc, builder);
    17         if (props == null) {
    18             props = new ArrayList<BeanPropertyWriter>();
    19         } else {
    20             props = removeOverlappingTypeIds(prov, beanDesc, builder, props);
    21         }
    22         
    23         // [databind#638]: Allow injection of "virtual" properties:
    24         prov.getAnnotationIntrospector().findAndAddVirtualProperties(config, beanDesc.getClassInfo(), props);
    25 
    26         // [JACKSON-440] Need to allow modification bean properties to serialize:
    27         if (_factoryConfig.hasSerializerModifiers()) {
    28             for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
    29                 props = mod.changeProperties(config, beanDesc, props);
    30             }
    31         }
    32 .......//此处省略
    33 }

      大家看一下props = mod.changeProperties(config, beanDesc, props);我们在配置类里面可是为我们的MappingJackson2HttpMessageConverter配置了 withSerializerModifier方法,并且设置了我们的MyBeanSerializerModifier并且继承BeanSerializerModifier重写了 changeProperties,所以会调用我们的changeProperties方法,进行修改null值的序列化类,我们也返回了一个list类型的BeanPropertyWriter,所以知道为什么那个BeanPropertyWriter在解析时,会是个数组类型的了吧,因为不同字段解析是不一样的。

    剩下的解释一下为什么单独配置并设置实例化@bean的MappingJackson2HttpMessageConverter也是好使的呢,大家可以看一下JacksonHttpMessageConvertersConfiguration类的源码,里面明确写了@ConditionalOnMissingBean注解,如果我们自己进行配置了,这个注入后就是一个备胎,以我们的为准,这个不多说

      我们再来解析一下第二个问题:为什么继承WebMvcConfigurationSupport后,再去生成@Bean的MappingJackson2HttpMessageConverter,却不生效,这需要跟第三个问题一起解决:为什么不继承WebMvcConfigurationSupport时,生成@Bean的MappingJackson2HttpMessageConverter是生效的。

      我们知道当我们继承WebMvcConfigurationSupport后,有一个配置会自动失效,就是自动注入的一个mvc配置,可以看看@SpringBootApplication注解里面有个@EnableAutoConfiguration注解,会引入一个AutoConfigurationImportSelector类。这个类就会扫描org.springframework.boot:spring-boot-autoconfigure下的spring.factories文件,这里面有一个我们默认的mvn配置也是继承了WebMvcConfigurationSupport,叫WebMvcAutoConfiguration,我们来看一下源码:

     1 @Configuration(proxyBeanMethods = false)
     2 @ConditionalOnWebApplication(type = Type.SERVLET)
     3 @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
     4 //注意此处有一个ConditionalOnMissingBean注解,所以如果我们自己继承后,就相当于已经存在WebMvcConfigurationSupport类,
     5 //就会走我们自己的配置类,此配置会失效
     6 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
     7 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
     8 @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
     9         ValidationAutoConfiguration.class })
    10 public class WebMvcAutoConfiguration {
    11     .....
    12 
    13     @Configuration(proxyBeanMethods = false)
    14     public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
    15 
    16         private final ResourceProperties resourceProperties;
    17 
    18         private final WebMvcProperties mvcProperties;
    19 
    20         private final ListableBeanFactory beanFactory;
    21 
    22         private final WebMvcRegistrations mvcRegistrations;
    23 
    24         private ResourceLoader resourceLoader;
    25 
    26         public EnableWebMvcConfiguration(ResourceProperties resourceProperties,
    27                 ObjectProvider<WebMvcProperties> mvcPropertiesProvider,
    28                 ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider, ListableBeanFactory beanFactory) {
    29             this.resourceProperties = resourceProperties;
    30             this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
    31             this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
    32             this.beanFactory = beanFactory;
    33         }
    34         //如果我们不继承的话,处理请求的RequestMappingHandlerAdapter就会在这里生成
    35         //会调用DelegatingWebMvcConfiguration里面的 requestMappingHandlerAdapter方法,
    36         @Bean
    37         @Override
    38         public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
    39                 @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
    40                 @Qualifier("mvcConversionService") FormattingConversionService conversionService,
    41                 @Qualifier("mvcValidator") Validator validator) {
    42             RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager,
    43                     conversionService, validator);
    44             adapter.setIgnoreDefaultModelOnRedirect(
    45                     this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
    46             return adapter;
    47         }
    48 
    49 .....
    50 }

      不知道大家是否还记得getMessageConverters()方法里面添加默认messageConverters的时候之前,会调用一个configureMessageConverters(this.messageConverters);方法,我们的DelegatingWebMvcConfiguration 就已经重写了这个方法,所以我们如果不继承WebMvcConfigurationSupport,将会把我们的@bean形式存在的MappingJackson2HttpMessageConverter将会被扫描到

     1 @Override
     2     protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
     3         this.configurers.configureMessageConverters(converters);
     4     }
     5 
     6 //会添加我们的convert
     7 @Override
     8     public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
     9         for (WebMvcConfigurer delegate : this.delegates) {
    10             delegate.configureMessageConverters(converters);
    11         }
    12     }

      现在我们配置的自定义jackson序列化已经生效了,但是,你仔细看我的流程图会发现,其实调用序列化的时候走的是RequestResponseBodyMethodProcessor的handleReturnValue方法

     1 @Override
     2     public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
     3             ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
     4             throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
     5 
     6         mavContainer.setRequestHandled(true);
     7         ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
     8         ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
     9 
    10         // Try even with null return value. ResponseBodyAdvice could get involved.
    11         //这里进入序列化流程
    12         writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    13     }

      最后在序列化的时候也是从这个类或则父类里面的一个属性:messageConverters

    public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
    
        private static final Set<HttpMethod> SUPPORTED_METHODS =
                EnumSet.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);
    
        private static final Object NO_VALUE = new Object();
    
    
        protected final Log logger = LogFactory.getLog(getClass());
        //这个属性取值的
        protected final List<HttpMessageConverter<?>> messageConverters;
    
        protected final List<MediaType> allSupportedMediaTypes;
    
        private final RequestResponseBodyAdviceChain advice;
    
    ...
    }

      于是,小伙伴们就疑惑了,这我们自定义的在RequestMappingHandlerAdapter里面呢,跟这个类也没关系啊,属性是咋设置进来的呢?我们再看看RequestMappingHandlerAdapter的源码,你会发现,RequestMappingHandlerAdapter这个类实现了InitializingBean类,也就说明,创建RequestMappingHandlerAdapter的时候会调用afterPropertiesSet方法,至于为啥,看源码吧:(不是主要流程)

     1 //在createBean的时候会调用这个方法,看看是否实现了InitializingBean
     2 protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
     3             throws Throwable {
     4 
     5         boolean isInitializingBean = (bean instanceof InitializingBean);
     6         if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
     7             if (logger.isTraceEnabled()) {
     8                 logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
     9             }
    10             if (System.getSecurityManager() != null) {
    11                 try {
    12                     AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
    13                         ((InitializingBean) bean).afterPropertiesSet();
    14                         return null;
    15                     }, getAccessControlContext());
    16                 }
    17                 catch (PrivilegedActionException pae) {
    18                     throw pae.getException();
    19                 }
    20             }
    21             else {
    22                 //在这里进行调用的,
    23                 ((InitializingBean) bean).afterPropertiesSet();
    24             }
    25         }
    26 
    27         if (mbd != null && bean.getClass() != NullBean.class) {
    28             String initMethodName = mbd.getInitMethodName();
    29             if (StringUtils.hasLength(initMethodName) &&
    30                     !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
    31                     !mbd.isExternallyManagedInitMethod(initMethodName)) {
    32                 invokeCustomInitMethod(beanName, bean, mbd);
    33             }
    34         }
    35     }
    invokeInitMethods

      那我们看看RequestMappingHandlerAdapter的afterPropertiesSet方法都干了些啥吧。

     1 @Override
     2     public void afterPropertiesSet() {
     3         // Do this first, it may add ResponseBody advice beans
     4         initControllerAdviceCache();
     5 
     6         if (this.argumentResolvers == null) {
     7             List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
     8             this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
     9         }
    10         if (this.initBinderArgumentResolvers == null) {
    11             List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
    12             this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    13         }
    14         if (this.returnValueHandlers == null) {
    15             //是在这里生成的类
    16             List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
    17             this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    18         }
    19     }
    private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
            List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();
    
            // Single-purpose return value types
            handlers.add(new ModelAndViewMethodReturnValueHandler());
            handlers.add(new ModelMethodProcessor());
            handlers.add(new ViewMethodReturnValueHandler());
            handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
                    this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
            handlers.add(new StreamingResponseBodyReturnValueHandler());
            handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
                    this.contentNegotiationManager, this.requestResponseBodyAdvice));
            handlers.add(new HttpHeadersReturnValueHandler());
            handlers.add(new CallableMethodReturnValueHandler());
            handlers.add(new DeferredResultMethodReturnValueHandler());
            handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
    
            // Annotation-based return value types
            handlers.add(new ModelAttributeMethodProcessor(false));
            //看到这个类了吗?生成的时候将RequestMappingHandlerAdapter里面的转换器设置进去了
            handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
                    this.contentNegotiationManager, this.requestResponseBodyAdvice));
    
            // Multi-purpose return value types
            handlers.add(new ViewNameMethodReturnValueHandler());
            handlers.add(new MapMethodProcessor());
    
            // Custom return value types
            if (getCustomReturnValueHandlers() != null) {
                handlers.addAll(getCustomReturnValueHandlers());
            }
    
            // Catch-all
            if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
                handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
            }
            else {
                handlers.add(new ModelAttributeMethodProcessor(true));
            }
    
            return handlers;
        }

      讲到这里,不知道大家理解了多少,这些都是博主遇到需求后,自己问自己的问题,自己通过源码回答问题的,也希望大家能理解源码。还有一篇源码文章在路上:为什么我们的项目里出现两个配置类继承WebMvcConfigurationSupport时,只有一个会生效。我在网上找了半天都是说结果的,没有人分析源码到底是为啥,博主准备讲解一下,希望可以帮到大家!


  • 相关阅读:
    20172311《程序设计与数据结构》第六周学习总结
    20172311 2018-2019-1 《程序设计与数据结构》课堂测试修改报告
    20172323 2018-2019-1《程序设计与数据结构》课程总结
    20172323 2018-2019-1 《程序设计与数据结构》课堂测试报告
    20172323 2018-2019-1 《程序设计与数据结构》实验三报告
    20172323 2018-2019-1 《程序设计与数据结构》第九周学习总结
    20172323 2018-2019-1 《程序设计与数据结构》实验二报告
    20172323 2018-2019-1 《程序设计与数据结构》第八周学习总结
    20172323 2018-2019-1 《程序设计与数据结构》第七周学习总结
    20172323 2018-2019-1 《程序设计与数据结构》第六周学习总结
  • 原文地址:https://www.cnblogs.com/guoxiaoyu/p/13667961.html
Copyright © 2011-2022 走看看