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


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());
}

跟踪这个实例化方法可以发现最终调用到了配置类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;
}

这个方法中当转换器为空的时候会添加默认的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;
}

上面注入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;
}

在调用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);
}

这个类通过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();
}

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

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

该方法将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);
		}
	}

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

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

@Configuration
@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
static class JacksonObjectMapperConfiguration {
	@Bean
	@Primary
	@ConditionalOnMissingBean
	public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
		return builder.createXmlMapper(false).build();
	}
}

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

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

@Bean
@Primary
public ObjectMapper customeObjectMapper() {
	ObjectMapper mapper = new ObjectMapper() ;
	//.....定义配置
	return mapper;
}

容器内置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);
		}
	}
}

这个重写这个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) {
			//省略代码.....
		}
		//省略方法.....
	}
}

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

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

  • 17
    点赞
  • 99
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值