首页 > 其他分享 >MappingJackson2HttpMessageConverter使用及jackson配置原理和避坑说明

MappingJackson2HttpMessageConverter使用及jackson配置原理和避坑说明

时间:2023-12-24 17:01:46浏览次数:38  
标签:jackson 自定义 messageConverters 配置 默认 避坑 转换器 MappingJackson2HttpMessageConverter O

转载自:https://blog.csdn.net/Heron22/article/details/109512976

MappingJackson2HttpMessageConverter


MappingJackson2HttpMessageConverter是springboot中默认的Json消息转换器。这个类的继承图如下:

 

在这里插入图片描述
这个类的主要实现逻辑是在AbstractJackson2HttpMessageConverter抽象类中实现的。这个列实现序列化与反序列化的最核心组件是ObjectMapper这个类。这些组件之间是如何协助,怎样才能合理的改写组件而实现自定义呢,这就需要了解起原理了。

消息转换器创建和生效原理

springboot Web项目中有两个重要的配置类需要知道。
一个是springmvc的原生配置类:WebMvcConfigurationSupport
另一个是springboot为springmvc写的自动配置类:WebMvcAutoConfiguration

这样区分的原因是前者本身就是springmvc项目的一部分,而后者是springboot实现自动装配写的配置类,有一些默认的约定的配,而且两者是不能同时生效的。两者的不通和一些坑可以具体点击这里查看文章介绍

使用这个两个不通配置类转换器有什么区别呢?看下图
在这里插入图片描述

如图所示,当使用WebMvcConfigurationSupport配置类的时候,为适配器单独创建了一个默认的转换器集合,而为容器注入的HttpMessageConverters对象也创建了一个默认的转换器集合的同时还增加了两个多添加了两个转换器。

使用WebMvcAutoConfiguration类的时候则适配器和HttpMessageConverters对象共同使用同一个转换器集合,另外也是比默认的适配器多了两个。

还要重点关注的是有两个MappingJackson2HttpMessageConverter转换器。

HttpMessageConverters对象的创建

配置类HttpMessageConvertersAutoConfiguration中注入了该对象,如下图:
在这里插入图片描述
HttpMessageConvertersAutoConfiguration配置类在实例化的时候注入了容器中所有的转换器实现类,并持有所有的实现类,同时注册了HttpMessageConverters类实例和StringHttpMessageConverter转换器实例。

查看HttpMessageConverters注入方法如下:

@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters() {
	return new HttpMessageConverters(
			this.converters != null ? this.converters : Collections.emptyList());
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

跟踪这个实例化方法可以发现最终调用到了配置类WebMvcConfigurationSupport的如下方法:

protected final List<HttpMessageConverter<?>> getMessageConverters() {
	if (this.messageConverters == null) {
		this.messageConverters = new ArrayList<>();
		configureMessageConverters(this.messageConverters);
		if (this.messageConverters.isEmpty()) {
			//添加默认的转换器(一共8个)
			addDefaultHttpMessageConverters(this.messageConverters);
		}
		extendMessageConverters(this.messageConverters);
	}
	return this.messageConverters;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这个方法中当转换器为空的时候会添加默认的8个转换器,最后与容器中注入的转换器合并形成完整的集合。

在JacksonHttpMessageConvertersConfiguration配置类中注入了一个MappingJackson2HttpMessageConverter转换器加上之前的,所以HttpMessageConverters中会多两个转换器。

使用WebMvcConfigurationSupport配置时转换器创建过程

那为什么使用WebMvcConfigurationSupport配置类的时候,会形成单独的两份转换器呢?在这个配置文件中会创建映射器适配器并注入到容器中,如下图:
在这里插入图片描述
它设置转换器组件的时候会调用到前文提到的getMessageConverters获取所有的转换器,如果此时转换器为空则会添加自定义转换器,如果添加到则不再添加默认转换器,使得默认转换器失效。

protected final List<HttpMessageConverter<?>> getMessageConverters() {
	if (this.messageConverters == null) {
		this.messageConverters = new ArrayList<>();
		//添加WebMvcConfigurer接口实现类configureMessageConvertersfang方法增加的自定义转换器(使默认转换器失效)
		configureMessageConverters(this.messageConverters);
		//存在自定义转换器,则默认不再添加默认转换器
		if (this.messageConverters.isEmpty()) {
			//添加默认的转换器(一共8个)
			addDefaultHttpMessageConverters(this.messageConverters);
		}
		//添加WebMvcConfigurer接口实现类extendMessageConverters方法增加的自定义转换器(在默认的基础上新增)
		extendMessageConverters(this.messageConverters);
	}
	return this.messageConverters;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

上面注入HttpMessageConverters类的时候会也会调用上述方法,效果是一样的,导致重新创建了一套转换器,同时有添加了容器中注入的转换器,所以也会多两个转换器。

使用WebMvcAutoConfiguration配置时转换器创建过程

该配置类中有一个内部类,这个类是WebMvcConfigurationSupport的子类,重写了映射器适配器注入方法,如下图:
在这里插入图片描述
实例化的时候调用了之前说的注入逻辑,也调用到了getMessageConverters获取所有的转换器的方法设置组件。

protected final List<HttpMessageConverter<?>> getMessageConverters() {
	if (this.messageConverters == null) {
		this.messageConverters = new ArrayList<>();
		//添加WebMvcConfigurer接口实现类configureMessageConvertersfang方法增加的自定义转换器(使默认转换器失效)
		configureMessageConverters(this.messageConverters);
		//存在自定义转换器,则默认不再添加默认转换器
		if (this.messageConverters.isEmpty()) {
			//添加默认的转换器(一共8个)
			addDefaultHttpMessageConverters(this.messageConverters);
		}
		//添加WebMvcConfigurer接口实现类extendMessageConverters方法增加的自定义转换器(在默认的基础上新增)
		extendMessageConverters(this.messageConverters);
	}
	return this.messageConverters;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在调用configureMessageConverters方法配置自定义转换器的时候,跟踪这个方法,最终调用到了DelegatingWebMvcConfiguration类的configureMessageConverters方法:

private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
	if (!CollectionUtils.isEmpty(configurers)) {
		this.configurers.addWebMvcConfigurers(configurers);
	}
}

@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
	this.configurers.configureMessageConverters(converters);
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这个类通过set方注入了WebMvcConfigurer接口的所有实现类。WebMvcAutoConfigurationAdapter类实现了这个接口,同时这个类在WebMvcAutoConfiguration配置类中是内部类,实现了注入,构造方法如下:

public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties,
		WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
		@Lazy HttpMessageConverters messageConverters,
		ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
	this.resourceProperties = resourceProperties;
	this.mvcProperties = mvcProperties;
	this.beanFactory = beanFactory;
	this.messageConverters = messageConverters;
	this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider
			.getIfAvailable();
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这个构造方法通过懒加载的形式注入HttpMessageConverters类实例并持有。所以上述的添加转换器时会调用WebMvcAutoConfigurationAdapter添加转换器的方法:

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
	converters.addAll(this.messageConverters.getConverters());
}
 
  • 1
  • 2
  • 3
  • 4

该方法将HttpMessageConverters类中持有的转换器全部添加到了映射器适配器中。再执行添加默认的转换器时候判断不为空,就不添加了,实际上是通过HttpMessageConverters类添加了。

所以当开启使用WebMvcAutoConfiguration配置类的时候,通过configureMessageConverters方法增加自定义转换器也是不会覆盖默认转换器的,但是如果想要调整自定义转换的位置顺序,可以实现order接口,默认配置的order是0

MappingJackson2HttpMessageConverter的配置

上述截图中这个转换器有两个,适配器中对应的那两个是哪里创建的呢

使用WebMvcConfigurationSupport配置时

使用这个配置时候适配器中只有一个JSON转换器,这个转换器是默认转换器中添加进来的,也就是适配器初始化的是new出来的。

默认转换器都是在方法里new出来的,所以要修改默认转换器的配置,只能通过在添加组件的接口WebMvcConfigurer的方法修改,最好是通过extendMessageConverters方法拿到所有转换器再修改。

使用WebMvcAutoConfiguration配置时

使用这个配置的时候
MappingJackson2HttpMessageConverter会在处理器适配器中持有两个,排在最前面的那个是通过容器创建注入的,第二个是上面的方式说的new出来的,所以用到的就是第一个。

MappingJackson2HttpMessageConverter注入流程

查看配置类JacksonHttpMessageConvertersConfiguration

@Configuration
class JacksonHttpMessageConvertersConfiguration {

	@Configuration
	@ConditionalOnClass(ObjectMapper.class)
	@ConditionalOnBean(ObjectMapper.class)
	@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jackson", matchIfMissing = true)
	protected static class MappingJackson2HttpMessageConverterConfiguration {

		@Bean
		@ConditionalOnMissingBean(value = MappingJackson2HttpMessageConverter.class, ignoredType = {
				"org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter",
				"org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter" })
		public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(
				ObjectMapper objectMapper) {
			return new MappingJackson2HttpMessageConverter(objectMapper);
		}
	}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

注入MappingJackson2HttpMessageConverter 的实现类需要从容器中注入ObjectMapper 实现类,这个类是实现json序列化和反序列化的核心,可能需要自定义一些配置。

再查看JacksonAutoConfiguration配置类有一个内部类如下:

@Configuration
@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
static class JacksonObjectMapperConfiguration {
	@Bean
	@Primary
	@ConditionalOnMissingBean
	public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
		return builder.createXmlMapper(false).build();
	}
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

可以看到这个内部类中注入了一个ObjectMapper 的对象,所以MappingJackson2HttpMessageConverter转换器使用的ObjectMapper对象就是这里创建的。

看这里注入使用@ConditionalOnMissingBean注解,也就是说如果我们要实现自定义的ObjectMapper来替换转换器中默认使用的,可以自己创建ObjectMapper对象注入容器中,实现完全的自定义。方式如下:

@Bean
@Primary
public ObjectMapper customeObjectMapper() {
	ObjectMapper mapper = new ObjectMapper() ;
	//.....定义配置
	return mapper;
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

容器内置ObjectMapper 也能满足绝大多数情景的使用,重写也就显得很不必要,但是有时候又需要修改一些ObjectMapper的配置。那如何实现呢?

看注入ObjectMapper的时候依赖注入了一个Jackson2ObjectMapperBuilder类对象。再查看JacksonAutoConfiguration配置类有如下内部类注入了我们需要的这个builder。

@Configuration
@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
static class JacksonObjectMapperBuilderConfiguration {
	private final ApplicationContext applicationContext;
	JacksonObjectMapperBuilderConfiguration(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}
	@Bean
	@ConditionalOnMissingBean
	public Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder(
			List<Jackson2ObjectMapperBuilderCustomizer> customizers) {
		Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
		builder.applicationContext(this.applicationContext);
		customize(builder, customizers);
		return builder;
	}
	private void customize(Jackson2ObjectMapperBuilder builder,
			List<Jackson2ObjectMapperBuilderCustomizer> customizers) {
		for (Jackson2ObjectMapperBuilderCustomizer customizer : customizers) {
			customizer.customize(builder);
		}
	}
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

这个重写这个builder类注入容器也可以实现ObjectMapper的自定义,但是上述注入的时候注入了Jackson2ObjectMapperBuilderCustomizer接口的所有实现类,所以可以通过类实现这个接口后注入容器中即可实现定制。

Jackson2ObjectMapperBuilderCustomizer接口定制ObjectMapper配置

再查看JacksonAutoConfiguration配置类中的内部类Jackson2ObjectMapperBuilderCustomizerConfiguration

@Configuration
@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
@EnableConfigurationProperties(JacksonProperties.class)
static class Jackson2ObjectMapperBuilderCustomizerConfiguration {

	@Bean
	public StandardJackson2ObjectMapperBuilderCustomizer standardJacksonObjectMapperBuilderCustomizer(
			ApplicationContext applicationContext,
			JacksonProperties jacksonProperties) {
		return new StandardJackson2ObjectMapperBuilderCustomizer(applicationContext,
				jacksonProperties);
	}

	private static final class StandardJackson2ObjectMapperBuilderCustomizer
			implements Jackson2ObjectMapperBuilderCustomizer, Ordered {

		private final ApplicationContext applicationContext;

		private final JacksonProperties jacksonProperties;

		StandardJackson2ObjectMapperBuilderCustomizer(
				ApplicationContext applicationContext,
				JacksonProperties jacksonProperties) {
			this.applicationContext = applicationContext;
			this.jacksonProperties = jacksonProperties;
		}

		@Override
		public int getOrder() {
			return 0;
		}

		@Override
		public void customize(Jackson2ObjectMapperBuilder builder) {
			//省略代码.....
		}
		//省略方法.....
	}
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

这里有默认的Jackson2ObjectMapperBuilderCustomizer接口实现类。这个类注入了JacksonProperties配置类,也就是配置文件spring.jackson配置项的内容,这也是我们再yml文件中配置就能修改转换器行为的原因。

**StandardJackson2ObjectMapperBuilderCustomizer这个实现类还实现了order接口,默认返回值是0 . 在自定义配置的时候,我们实现接口的同时也应该实现order接口,同时也要将创建级别调小(也就是大于0).因为这样自定义配置可以排在默认实现类后生效,从而覆盖默认配置。**不实现order接口,默认最低级别也是可以的。

标签:jackson,自定义,messageConverters,配置,默认,避坑,转换器,MappingJackson2HttpMessageConverter,O
From: https://www.cnblogs.com/wanghengbin/p/17924597.html

相关文章

  • 放弃FastJson!一篇就够,Jackson的功能原来如此之牛(万字干货)
    放弃FastJson!一篇就够,Jackson的功能原来如此之牛(万字干货)转载自:https://zhuanlan.zhihu.com/p/352485162在上篇《经过多方调研,最终还是决定禁用FastJson!》中,讲了FastJson的基本使用以及存在的不确定性问题,所以最终决定在项目中放弃使用,进而选择市面上比较主流,SpringBoot默......
  • Jackson Annotations(注解)详解
    转载自:https://blog.csdn.net/wjw465150/article/details/1273268491.概述在本教程中,我们将深入研究JacksonAnnotations。我们将了解如何使用现有的注解,如何创建自定义注解,最后,如何禁用它们。2.Jackson序列化注解首先,我们将看一下序列化注解。2.1.@JsonAnyGetter@J......
  • Jackson给给指定类设置序列化规则
    背景业务中需要对返回给APP端的数据进行特殊处理,包括:null值转换成空字符串日期(LocalDateTime)类型转换成时间戳金额根据用户Locale做格式化需要保证不影响内部其他服务的互相调用,因此让所有返回给APP的VO对象实现自定义的Vo接口,然后指定对Vo接口的类进行对应的转换。实现co......
  • 避坑合集|芝麻免押失败排查思路超详细总结(小程序场景)
    在使用小程序对接芝麻免押时,遇到了一些报错,估计把这个接口的坑都踩了个遍,这篇汇总一下我在芝麻免押上遇到的免押失败问题合集,大家注意避坑~对接流程......
  • 避坑合集|芝麻免押失败排查思路超详细总结(小程序场景)
    在使用小程序对接芝麻免押时,遇到了一些报错,估计把这个接口的坑都踩了个遍,这篇汇总一下我在芝麻免押上遇到的免押失败问题合集,大家注意避坑~对接流程......
  • mongodb避坑
    1.首先MongoDB6.0及以上的版本是不带mongoshell的,所以要向用需要自己去下载,然后将压缩包解压到桌面然后复制过去一定是复制过去,要不然mongo的管理员权限不让你复制,  完成之后在这个文件加下的bin中双击后回车就可进入shell界面。2.然后是API操作,这里用的是maven,我们将代......
  • 百度工程师移动开发避坑指南——Swift语言篇
    百度工程师移动开发避坑指南——Swift语言篇百度Geek说 ​关注 1人赞同了该文章作者|启明星小组上一篇我们介绍了移动开发常见的内存泄漏问题,见《百度工程师移动开发避坑指南——内存泄漏篇》。本篇我们将介绍Swift语言部分常见问题。对于......
  • Jackson记录
    1.基本使用,序列化和反序列化序列化publicclassJacksonTest{staticObjectMapperobjectMapper=newObjectMapper();publicstaticvoidmain(String[]args)throwsJsonProcessingException{Personperson=newPerson();person.setName("ja......
  • 汽车托运如何完美避坑
    在现代社会中,汽车托运已经成为了一种常见的服务。无论是因为搬家、旅游还是其他原因,许多人都会选择将汽车托运到目的地。然而,由于市场上的服务质量参差不齐,很多人在托运过程中遇到了各种问题,甚至有些人因此遭受了损失。那么,如何在汽车托运过程中避免掉入这些“坑”呢?以下是一些实......
  • jackson 常用注解学习
    User.javapackageorg.example.entity;importcom.fasterxml.jackson.annotation.JsonFormat;importcom.fasterxml.jackson.annotation.JsonIgnore;importcom.fasterxml.jackson.annotation.JsonInclude;importcom.fasterxml.jackson.annotation.JsonProperty;impo......