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