最近项目要实现一种需求,对于后端返回给前端的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. Java中的判断实例

    .getClass().getName() 这是最常见的一种判断类型的方法 instanceof 用于判断 对象 是否为某个类的实例 Boolean值 各种is方法 isAnnotationPrese ...

  2. mysql再回首

    Mysql与Oracle的区别 1.实例区别 Mysql是一个轻量型数据库,开源免费.Oracle是收费的而且价格非常高. Mysql一个实例可以操作多个库,而Oracle一个实例只能对应一个库. M ...

  3. 新手oracle重启、监听

    有一次遇到了记录下. #su到oracle用户下 [root@localhost ~]# su - oracle  #重启数据库:[oracle@localhost ~]$ sqlplus /nolo ...

  4. Photon Server伺服务器在LoadBalancing的基础上扩展登陆服务

    一,如何创建一个Photon Server服务 参见此博客 快速了解和使用Photon Server 二, 让LoadBalancing与自己的服务一起启动 原Photonserver.config文 ...

  5. 【C#】Random类中构造方法、时间种子与随机数序列的关系

    Random类 构造函数 1) Random random = new Random(); // 无参数构造函数使用系统时钟生成其种子值 然而,系统时钟取值范围有限,因此在小规模计算中,可能无法使用不 ...

  6. 分享一款知识库平台系统-wcp

    入园这么些天了,今天搭建了一套知识库系统,使用效果还不错,分享一些过程经验. 搭建准备: 软件系统:WCP4.3免费版 (免费开源,支持Windows,使用简单,有傻瓜式一键安装包-win平台) 服务 ...

  7. 非IT行业大企程序员讲述MIS系统开发案例

      雪莉叹了一口气,调整了一下被汗水濡湿的刘海,然后向后靠在办公椅中,伸手在电脑键盘上输入了一些内容, 最后拿起印刷着房地产广告的扇子,边扇风边等待着.   她的工位在办公室的东侧角落,侧靠着窗.此时 ...

  8. Activiti7 使用监听器分配任务人员

    视屏中老师说,一般没有人用但是我还是想试试 但是当我画图的时候,发现IDEA的那个listener监听器点不开,不知道是不是我下载的插件不对还是什么原因,所以就亲自写了,看看到时候不行就下载一个Ecl ...

  9. leetcode刷题-71简化路径

    题目 以 Unix 风格给出一个文件的绝对路径,你需要简化它.或者换句话说,将其转换为规范路径. 在 Unix 风格的文件系统中,一个点(.)表示当前目录本身:此外,两个点 (..) 表示将目录切换到 ...

  10. elasticsearch跨集群数据迁移

    写这篇文章,主要是目前公司要把ES从2.4.1升级到最新版本7.8,不过现在是7.9了,官方的文档:https://www.elastic.co/guide/en/elasticsearch/refe ...