源码分析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时,只有一个会生效。我在网上找了半天都是说结果的,没有人分析源码到底是为啥,博主准备讲解一下,希望可以帮到大家!