SpringBoot对Jackson做了什么自动配置

前言

SpringBoot支持哪些Json框架的自动配置?

我们之前了解过,SpringBoot的自动配置在autoconfigue的jar包中定义。可以从中发现,SpringBoot至少可以支持配置三种Json框架:

  1. jsckson :org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
  2. gson :org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration
  3. jsonb:org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration

为什么SpringBoot默认使用的是Jackson 框架?

这是因为在spring-boot-starter-web
依赖包中已经依赖了Jackson的依赖包jackson-databind,在Jackson中的自动配置类中,有一个条件:@ConditionalOnClass(ObjectMapper.class),简单来说,就是在当前项目的classpath中找到ObjectMapper.class,才会构建这个bean,刚好这个类是在jackson-databind的包中的,因此使得Jackson变成了Springboot的默认Json处理器。

Jackson比较重要的自动配置

首先研究一下Jackson的自动配置类:JacksonObjectMapperConfiguration
里面的所有内部配置类,都是有一个条件:@ConditionalOnClass(Jackson2ObjectMapperBuilder.class),这个Jackson2ObjectMapperBuilder是在spring-web.jar中的,所以可以简单认为,只要是web的环境下,都会自动加载这几个配置类。

JacksonObjectMapperConfiguration

功能:往Spring容器中注册一个ObjectMapper的实例,需要用到Jackson2ObjectMapperBuilder 实例

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

ObjectMapper在SpringBoot中有什么用?

在Spring Boot Web 项目中,当使用JSON格式接收数据和返回数据的时候,Spring Boot 默认使用一个ObjectMapper实例来序列化响应和反序列化请求。

与@ResponseBody的关系:
HttpMessageConverter是Spring3.0新添加的一个接口,负责将请求信息转换为一个对象(类型为T),将对象(类型为T)输出为响应信息
当加入了Jackson的jar包之后,DispatchServlet会自动装配一个MappingJackson2HttpMessageConverter
ResponseBody注解就是利用这个Converter来实现JSON化的。

MappingJackson2HttpMessageConverter的构造方法就要求传入一个ObjectMapper实例。

题外话:这个MappingJackson2HttpMessageConverter具体是怎么注册到Spring容器中的?
首先,可以通过autoconfigure这个包,查看到所有的HttpMessasgeConverter都是由HttpMessageConvertersAutoConfiguration这个配置类进行注册的。
通过@Import处找到JacksonHttpMessageConvertersConfiguration,可以看见SpringBoot在Jackson包引入的情况已经为我们初始化了一个mappingJackson2HttpMessageConverter 的bean。

@Configuration(proxyBeanMethods = false)
class JacksonHttpMessageConvertersConfiguration {

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

		@Bean
		@ConditionalOnMissingBean(value = MappingJackson2HttpMessageConverter.class,
				ignoredType = {
						"org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter",
						"org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter" })
		MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
			return new MappingJackson2HttpMessageConverter(objectMapper);
		}

	}
}

JacksonObjectMapperBuilderConfiguration

JacksonObjectMapperConfiguration->JacksonObjectMapperBuilderConfiguration类
功能:
在上面的JacksonObjectMapperConfiguration 可以知道,要构造ObjectMapper,就需要Jackson2ObjectMapperBuilder 的实例。
JacksonObjectMapperBuilderConfiguration 就是用来注册Jackson2ObjectMapperBuilder 的实例。
而且通过customize方法可以知道,通过实现Jackson2ObjectMapperBuilderCustomizer 就可以对Jackson2ObjectMapperBuilder 实例实现某些自定义化功能。这里暂时先不介绍

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
static class JacksonObjectMapperBuilderConfiguration {

    
    @Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder(ApplicationContext applicationContext,
                                                           List<Jackson2ObjectMapperBuilderCustomizer> customizers) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        builder.applicationContext(applicationContext);
        customize(builder, customizers);
        return builder;
    }

    private void customize(Jackson2ObjectMapperBuilder builder,
                           List<Jackson2ObjectMapperBuilderCustomizer> customizers) {
        for (Jackson2ObjectMapperBuilderCustomizer customizer : customizers) {
            customizer.customize(builder);
        }
    }

}

Jackson2ObjectMapperBuilderCustomizerConfiguration

JacksonObjectMapperConfiguration->Jackson2ObjectMapperBuilderCustomizerConfiguration
作用:
通过配置文件配置JacksonProperties 的属性来实现定制化

在上面介绍JacksonObjectMapperBuilderConfiguration 的配置类时说到过,通过实现Jackson2ObjectMapperBuilderCustomizer 就可以对Jackson2ObjectMapperBuilder 实例实现某些自定义化功能。

下面注册的StandardJackson2ObjectMapperBuilderCustomizer 就是一个Jackson2ObjectMapperBuilderCustomizer 的实现。

static class Jackson2ObjectMapperBuilderCustomizerConfiguration {
        //...

		@Bean
		StandardJackson2ObjectMapperBuilderCustomizer standardJacksonObjectMapperBuilderCustomizer(
				ApplicationContext applicationContext, JacksonProperties jacksonProperties) {
			return new StandardJackson2ObjectMapperBuilderCustomizer(applicationContext, jacksonProperties);
		}
    
        @Override
        public void customize(Jackson2ObjectMapperBuilder builder) {
    
            if (this.jacksonProperties.getDefaultPropertyInclusion() != null) {
                builder.serializationInclusion(this.jacksonProperties.getDefaultPropertyInclusion());
            }
            if (this.jacksonProperties.getTimeZone() != null) {
                builder.timeZone(this.jacksonProperties.getTimeZone());
            }
            configureFeatures(builder, FEATURE_DEFAULTS);
            configureVisibility(builder, this.jacksonProperties.getVisibility());
            configureFeatures(builder, this.jacksonProperties.getDeserialization());
            configureFeatures(builder, this.jacksonProperties.getSerialization());
            configureFeatures(builder, this.jacksonProperties.getMapper());
            configureFeatures(builder, this.jacksonProperties.getParser());
            configureFeatures(builder, this.jacksonProperties.getGenerator());
            configureDateFormat(builder);
            configurePropertyNamingStrategy(builder);
            configureModules(builder);
            configureLocale(builder);
        }
    //...
}

Jackson自定义行为

方式一:通过配置文件去自定义Jackson的行为

通过上面的自动配置分析,实际上配置文件的方式也是通过Jackson2ObjectMapperBuilderCustomizer 接口来实现自定义的。
applicaton.yml
此模板参考:https://blog.csdn.net/qq_43842093/article/details/121866953

spring:
  jackson:
    # 设置属性命名策略,对应jackson下PropertyNamingStrategy中的常量值,
    # SNAKE_CASE-返回的json驼峰式转下划线,json body下划线传到后端自动转驼峰式
    property-naming-strategy: SNAKE_CASE
    # 全局设置@JsonFormat的格式pattern
    date-format: yyyy-MM-dd HH:mm:ss
    # 当地时区
    locale: zh
    # 设置全局时区
    time-zone: GMT+8
    # 常用,全局设置pojo的属性的序列化方式
    #不为空的属性才会序列化,具体属性可看JsonInclude.Include
    default-property-inclusion: NON_NULL 
    # 常规默认,枚举类SerializationFeature中的枚举属性为key,
    # 值为boolean设置jackson序列化特性,具体key请看SerializationFeature源码
    serialization:
      WRITE_DATES_AS_TIMESTAMPS: true # 返回的java.util.date转换成timestamp
      FAIL_ON_EMPTY_BEANS: true # 对象为空时是否报错,默认true
    # 枚举类DeserializationFeature中的枚举属性为key,
    # 值为boolean设置jackson反序列化特性,具体key请看DeserializationFeature源码
    deserialization:
      # 常用,json中含pojo不存在属性时是否失败报错,默认true
      FAIL_ON_UNKNOWN_PROPERTIES: false
    # 枚举类MapperFeature中的枚举属性为key,值为boolean设置jackson ObjectMapper特性
    # ObjectMapper在jackson中负责json的读写、json与pojo的互转、
    # json tree的互转,具体特性请看MapperFeature,常规默认即可
    mapper:
      # 使用getter取代setter探测属性,如类中含getName()但不包含name属性与setName(),
      # 传输的vo json格式模板中依旧含name属性
      USE_GETTERS_AS_SETTERS: true #默认false
      # 枚举类JsonParser.Feature枚举类中的枚举属性为key,值为boolean设置jackson JsonParser特性
      # JsonParser在jackson中负责json内容的读取,具体特性请看JsonParser.Feature,一般无需设置默认即可
    parser:
      ALLOW_SINGLE_QUOTES: true # 是否允许出现单引号,默认false
      # 枚举类JsonGenerator.Feature枚举类中的枚举属性为key,值为boolean设置jackson JsonGenerator特性,一般无需设置默认即可
            # JsonGenerator在jackson中负责编写json内容,具体特性请看JsonGenerator.Feature

下面详细介绍几个我觉得平时可以用得上的

  1. property-naming-strategy
    a. 作用
    ⅰ. 用于定义响应对象->JSON以及JSON->请求对象的属性名匹配方法,例如 小驼峰->下划线
    b. 策略介绍:都是对应jackson下PropertyNamingStrategy中的常量值
    ⅰ. SNAKE_CASE: 响应对象驼峰转换为下划线,请求对象下划线转首字母小写驼峰,下面的策略均是如此,如 ageList - > age_list
    ⅱ. UPPER_CAMEL_CASE 驼峰转换为首字母大写的驼峰,如 ageList - > ageList
    ⅲ. LOWER_CAMEL_CASE 驼峰转换为首字母小写的驼峰,如 ageList - > AgeList
    ⅳ. LOWER_CASE 驼峰转全小写,如 ageList - > age-list
    ⅴ. KEBAB_CASE 驼峰转横杠,如 ageList - > age-list
    ⅵ. LOWER_DOT_CASE 驼峰转逗号,如:ageList-> age.list

  2. date-format
    a. 作用:全局设置@JsonFormat的格式,@JsonFormat是用于定义响应对象中Date->String或者LocalDateTime->String的Json字符串格式,目前一般不用这种@JsonFormat方式,因为需要在每个使用到Date类型的地方注解,特别麻烦,还不如定义全局的反序列化器然后注册到Jackson的ObjectMapper中。
    b. 这里发现个坑,配置文件中定义的date-format,只能应用于Date类型,没办法使用于LocalDateTime,但是如果在@JsonFormat中直接指定pattern,LocalDateTime是可以转换的-_-||

  3. default-property-inclusion
    a. 首先介绍一个注解:@JsonInclude,这个注解用于设置序列化的条件,策略枚举类为JsonInclude
    ⅰ. ALWAYS 总是参与序列化,这个是默认指定的
    ⅱ. NON_NULL 不是为null才参与序列化
    ⅲ. NON_ABSENT 为缺省值或者为 NULL不会参与序列化,专用于Optional
    ⅳ. NON_EMPTY 为空字符串或者为 NULL不会参与序列化
    b. 作用:而这个属性则是全局指定的序列化策略,满足指定条件才会序列化某个字段,这个可以看情况使用,也可以使用注册自定义的序列化器到ObjectMapper实现类似的功能
    c. 坑:这个并不是类似date-format,它不需要配合注解使用,如果使用了@JsonInclude ,就会直接按@JsonInclude 指定的方式对字段序列化,而不是使用全局的序列化方式

  4. serialization
    a. 作用:设置序列化时的特性,可设置值:key为SerializationFeature枚举,value是Boolean,代表是否启用,一般应用在接口的响应对象里面
    b. 枚举介绍(只介绍部分)
    ⅰ. 输出特性

    1. WRAP_ROOT_VALUE:在响应对象的最外层再添加一个根部的对象JSON层级
      a. 如:{“age”:13} 变成 {“JsonResult”:{“age”:13}},根部json层级的属性名默认是响应对象的类名
    2. INDENT_OUTPUT:美化响应内容的JSON,不用一整串的返回给前端(-_-||),不过swagger自带美化功能,不影响开发
      ⅱ. 错误处理特性
    3. WRITE_SELF_REFERENCES_AS_NULL:如果出现自身引用自身的情况,则把成员对象变成null,例如Book的成员对象里有Book类型,默认为false,用的时候要把FAIL_ON_SELF_REFERENCES改为false,不然试不出来(不过用到的情况也很少…)
      ⅲ. 指定特定类型序列化的特性
    4. WRITE_DATES_AS_TIMESTAMPS:默认开启,Date类型序列化为时间戳(13位),(LocalDate和LocalDateTime会变成年,月,日,时,分,秒的数组)
    5. WRITE_DATE_KEYS_AS_TIMESTAMPS:默认关闭,当使用Map<Date,Object>的时候,Date的key值序列化之后变成时间戳(13位)
    6. WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS:默认关闭,char[] 转换为一个JSON数组,其中每个元素都是一个String值
    7. WRITE_ENUMS_USING_TO_STRING:默认关闭,应用于枚举类,如果关闭状态,则返回Enum.name(),即名称,开启时返回toString().如果没有重写的话,其实都是一样的作用。
    8. WRITE_ENUMS_USING_INDEX:默认关闭,开启时返回枚举的ordinal() 值。
    9. WRITE_ENUM_KEYS_USING_INDEX:默认关闭,应用于以Enum为key的map。开启时会序列化为枚举的ordinal() 值作为key值
    10. WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED:默认关闭,开启时,响应对象的集合或数组中当前仅有一个元素时,将元素的属性往上提一次,也就是这个数组[]变成了{}
  5. deserialization
    a. 作用:设置反序列化时的特性,可设置值:key为DeserializationFeature枚举,value是Boolean,代表是否启用,一般应用在接口的请求对象里面
    b. 枚举介绍,我觉得用到的场景不是很多

    1. DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES 在反序列化时,忽略未知字段
      1. 最近碰到过转化的问题了,举个例子:{“code”:4003,“successful”:true}, 如果接收的类型中没有successful的话,会出现异常,提示未知字段【“successful”】,这时候设置为false就会忽略这个未知字段
  6. mapper
    a. 作用: 枚举类MapperFeature中的枚举属性为key,值为boolean设置jackson ObjectMapper特性,ObjectMapper在jackson中负责json的读写、json与pojo的互转,所以该属性是设置json与pojo的互转策略
    b. 策略举例:
    ⅰ. USE_GETTERS_AS_SETTERS: true :默认false,使用getter取代setter探测属性,如类中含getName()但不包含name属性与setName(),传输的vo json格式模板中依旧含name属性

  7. parser
    a. 作用:枚举类JsonParser.Feature枚举类中的枚举属性为key,值为boolean设置jackson JsonParser特性,JsonParser在jackson中负责json内容的读取,具体特性请看JsonParser.Feature,一般无需设置默认即可

方式二:通过实现Jackson2ObjectMapperBuilderCustomizer 来自定义Jackson的行为

什么时候需要用到这种方式?
当jackson提供的配置不满足生产需求时,就需要使用这种方式去自定义。

Jackson2ObjectMapperBuilderCustomizer 里面也就是一个函数式接口,基于customize 方法对Jackson2ObjectMapperBuilder实例进行设置。

@FunctionalInterface
public interface Jackson2ObjectMapperBuilderCustomizer {
    void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder);
}

那么,Jackson2ObjectMapperBuilder 又提供了什么可以让我们设置呢?
我在平时开发中用到以下的设置API:

  1. 自定义反序列化
    a. public Jackson2ObjectMapperBuilder deserializers(JsonDeserializer<?>... deserializers) b. public Jackson2ObjectMapperBuilder deserializerByType(Class<?> type, JsonDeserializer<?> deserializer)
    c. 上面两个注册反序列化器本质上没有区别,只是第一个API由于没有指定type,所以在JsonDeserializer中必须要重写handleType方法指定作用的类型
  2. 自定义序列化器
    a. public Jackson2ObjectMapperBuilder serializers(JsonSerializer<?>... serializers) b. public Jackson2ObjectMapperBuilder serializerByType(Class<?> type, JsonSerializer<?> serializer)
    c. 同反序列化器的API,上面两个注册序列化器本质上没有区别,只是第一个API由于没有指定type,所以在JsonDeserializer中必须要重写handleType方法指定作用的类型

由于配置没有支持修改日期Date,LocaleDateTime,LocalDate的序列化和反序列化规则,所以只好使用以上API去自定义。
对于LocalDateTime和LocalDate的序列化,如果只是定义字符串格式,除了自定义自己的序列化器之外,还可以使用LocalDateTimeSerializer 和LocalDateSerializer 指定格式,这样的话就不需要新写一个类

除此之外,可以通过实现自定义的序列化器,在序列化时去除字符串前后空格,也就是实现一个trim的功能

然后还有其他的方法,例如:

  1. public Jackson2ObjectMapperBuilder indentOutput(boolean indentOutput)
  2. public Jackson2ObjectMapperBuilder dateFormat(DateFormat dateFormat)
    都是配置的延伸,有兴趣可以了解一下

参考

https://developer.51cto.com/article/712411.html

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值