最近项目要实现一种需求,对于后端返回给前端的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时,只有一个会生效。我在网上找了半天都是说结果的,没有人分析源码到底是为啥,博主准备讲解一下,希望可以帮到大家!


源码分析springboot自定义jackson序列化,默认null值个性化处理返回值的更多相关文章

  1. asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证

    原文:asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证 在前面的文章中我们曾经涉及到ControllerActionInvoker类GetPara ...

  2. JUnit源码分析 - 扩展 - 自定义RunListener

    RunListener简述 JUnit4中的RunListener类用来监听测试执行的各个阶段,由RunNotifier通知测试去运行.RunListener与RunNotifier之间的协作应用的是 ...

  3. ArrayList 源码分析和自定义ArrayList实现

    概述 ArrayList 是基于数组实现的,是一个能自动扩展的动态数组. ArrayList 是线程不安全的,多线程情况下添加元素会出现数组越界的情况,而且数组赋值操作不是原子操作,会导致多线程情况下 ...

  4. Netty源码分析之自定义编解码器

    在日常的网络开发当中,协议解析都是必须的工作内容,Netty中虽然内置了基于长度.分隔符的编解码器,但在大部分场景中我们使用的都是自定义协议,所以Netty提供了  MessageToByteEnco ...

  5. Django(63)drf权限源码分析与自定义权限

    前言 上一篇我们分析了认证的源码,一个请求认证通过以后,第二步就是查看权限了,drf默认是允许所有用户访问 权限源码分析 源码入口:APIView.py文件下的initial方法下的check_per ...

  6. Django(64)频率认证源码分析与自定义频率认证

    前言 有时候我们发送手机验证码,会发现1分钟只能发送1次,这是做了频率限制,限制的时间次数,都由开发者自己决定 频率认证源码分析 def check_throttles(self, request): ...

  7. JUnit源码分析 - 扩展 - 自定义Rule

    JUnit Rule简述 Rule是JUnit 4.7之后新加入的特性,有点类似于拦截器,可以在测试类或测试方法执行前后添加额外的处理,本质上是对@BeforeClass, @AfterClass, ...

  8. Django(60)Django内置User模型源码分析及自定义User

    前言 Django为我们提供了内置的User模型,不需要我们再额外定义用户模型,建立用户体系了.它的完整的路径是在django.contrib.auth.models.User. User模型源码分析 ...

  9. [Abp vNext 源码分析] - 5. DDD 的领域层支持(仓储、实体、值对象)

    一.简要介绍 ABP vNext 框架本身就是围绕着 DDD 理念进行设计的,所以在 DDD 里面我们能够见到的实体.仓储.值对象.领域服务,ABP vNext 框架都为我们进行了实现,这些基础设施都 ...

随机推荐

  1. 可爱的python

    可爱的python 作者:  哲思社区出版社: 电子工业出版社 优点 1. 案列讲解很详细,前几章的内容恰好是我想要了解的,例如利用python os模块读取磁盘的文件,或者获得文字的编码方式.这些内 ...

  2. Java动态代理(二)——jdk动态代理

    一.什么是动态代理?代理类在程序运行时创建的代理方式被成为动态代理.动态代理的代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的.相比于静态代理, 动态代理的 ...

  3. LR与LR?

    目录 逻辑回归与线性回归 逻辑回归 1.建立目标函数 2. 梯度求解 3. 实现 线性回归 1. 建立目标函数 2. 求解 3. 实现 逻辑回归与交叉熵 逻辑回归与线性回归 逻辑回归 线性回归 目标函 ...

  4. FinalShell远程连接工具推荐

    今天给大家推荐一个类似Xshell的工具FinalShell,这个工具也使用了很长时间了, Windows和Mac版本都有,方便连接虚拟机 可以很方便的上传文件,有兴趣可以试试这款软件. 地址:htt ...

  5. .NET Core3.1 Dotnetty实战第二章

    一.概要 在上一篇文章讲到Dotnetty的基本认识,本文这次会讲解dotnetty非常核心的模块是属于比较硬核的干货了,然后继续往下讲解如何根据自己的需求或者自己的喜好去配置Dotnetty而不是生 ...

  6. csp201909-2小明种苹果续

    /* 定义输入N 二维数组 输出T总数 D掉落棵树 E掉落组数 定义last记录上次掉落的编号,flag=1表示两次连续掉落,不掉落归零 spec=1表示1 2都掉落了,spec=2表示只有1掉落 对 ...

  7. 释放数据价值:DAYU数据运营新能力解读

    摘要:从比特到信息,这说的其实就是企业数字化转型,让数据的价值充分发挥出来,变成信息. 今天,企业对数据越来越重视,数据已经成为了企业新型的资产,甚至是核心资产,最近流传一句非常有意思的话:从比特到信 ...

  8. oeasy教您玩转linux-010110内容回顾

    我们来回顾一下 我们都讲了什么?

  9. 05_进程间通信 IPC

    1.进程间的通信方式 1.磁盘交互: 速度慢,不安全 2.socket套接字 3.管道通信(Pipe) 4.消息队列(Queue, Manager().Queue, JoinableQueue) 5. ...

  10. 启动Tomcat服务器端口被占用解决方法

    Caused by: java.net.BindException: Address already in use: bind 1.输入 netstat -ano|findstr 8080,回车,显示 ...