0


spring boot 七:SpringBoot自定义配置Jackson的ObjectMapper

spring boot 七:SpringBoot2.5.4自定义配置Jackson的ObjectMapper

1 前言

SpringBoot底层默认使用的自动依赖注入,即spring-boot-autoconfigure包的META-INF下,存在spring.factories文件,里面有自动注入的jackson自动配置类。在EnableAutoConfiguration的配置下,名为JacksonAutoConfiguration。根据对该自动配置实施自定义Bean配置,可实现对@ResponseBody或@RestController注解下的响应结果的全局序列化jackson配置。

比如SpringBoot默认的json类型返回中,new Date() 类型数据返回的时间戳,LocalDateTime类型的格式化不美观等等,可自定义jackson objectMapper全局配置解决。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

SpringBoot参考文档(版本为SpringBoot2.5.14文档,实际使用的SpringBoot依赖版本2.5.4,参考实际源码即可):

https://docs.spring.io/spring-boot/docs/2.5.14/reference/html/features.html#features.json

https://docs.spring.io/spring-boot/docs/2.5.14/reference/html/howto.html#howto.spring-mvc.customize-jackson-objectmapper

依赖配置:

<parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.boot</groupId><version>2.5.4</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies>

2 使用

2.1 自定义Jackson ObjectMapper配置类:

使用前,先观察下部分源码:

启动类的@SpringBootApplication注解中,包含了@EnableAutoConfiguration注解,而@EnableAutoConfiguration注解上,又使用@Import导入了资源类AutoConfigurationImportSelector.class:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

针对SpringApplication.run(MainApplication.class, args)进行debug:

在SpringApplication下的refreshContext打上断点:

在这里插入图片描述

一直debug到AutoConfigurationImportSelector的getAutoConfigurationEntry方法,执行筛选后,仅剩24个自动配置类,其中就包含jackson的自动配置类JacksonAutoConfiguration:

在这里插入图片描述

由此根据源码JacksonAutoConfiguration的配置,可做如下自定义的配置:

jackson的ObjectMapper全局配置类如下,其中ObjectMapper配置非单例,每次Spring的上下文获取该objectMapper bean时,均是重新生成的实例对象:

packagecom.xiaoxu.test.springBoot.configure;importcom.fasterxml.jackson.annotation.JsonInclude;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.fasterxml.jackson.databind.PropertyNamingStrategy;importcom.fasterxml.jackson.databind.SerializationFeature;importorg.springframework.beans.factory.annotation.Qualifier;importorg.springframework.boot.autoconfigure.condition.ConditionalOnBean;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.context.annotation.Primary;importorg.springframework.context.annotation.Scope;importorg.springframework.http.converter.json.Jackson2ObjectMapperBuilder;importjava.text.SimpleDateFormat;/**
 * @author xiaoxu
 * @date 2022-12-30
 * java_demo:com.xiaoxu.test.springBoot.configure.JacksonGlobalConfig
 */@ConfigurationpublicclassJacksonGlobalConfig{@Bean(name ="jackson2ObjectMapperBuilder")/* 观察SpringBoot-2.5.4
    * jackson的自动配置类:JacksonAutoConfiguration 源码
    *  */@SuppressWarnings(value ="   deprecation ")publicJackson2ObjectMapperBuilderxiaoxuJackson2ObjectMapperBuilder(){System.out.println("1.jackson builder注入:");Jackson2ObjectMapperBuilder builder =newJackson2ObjectMapperBuilder();
        builder.serializationInclusion(JsonInclude.Include.NON_NULL);
        builder.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
        builder.featuresToEnable(SerializationFeature.WRAP_ROOT_VALUE);/* 针对new Date  SimpleDateFormat线程不安全, 建议使用自定义的日期工具类,
        * 此处只做简单演示配置*//* yyyy-MM-dd HH:mm:ss.SSS *//* 使用 dateFormat, new Date 和 LocalDateTime 均会产生对应效果*//* new Date 不设置的情况下,默认返回时间戳 */
        builder.dateFormat(newSimpleDateFormat("yyyy-MM-dd HH:mm:ss"));return builder;}@Bean(name ="jacksonObjectMapper")@Primary@ConditionalOnBean(name ={"jackson2ObjectMapperBuilder"})/* Jackson的ObjectMapper 是线程安全的, 不过SpringBoot2.5.4源码上使用的是非单例模式,这里和源码保持一致 */@Scope("prototype")//    @Scope("singleton")publicObjectMapperxiaoxuJacksonObjectMapper(@Qualifier("jackson2ObjectMapperBuilder")Jackson2ObjectMapperBuilder builder){System.out.println("2.jackson objectMapper注入:");return builder.createXmlMapper(false).build();}}

因为jackson默认序列化json数据是会展示为null的数据,所以上述修改后的全局配置,去掉了为null数据的展示,以及将实体类的小驼峰Field样式改为了SNAKE_CASE下划线样式,并且增加了实体类的根key(这里为class name)的展示,还有日期的格式化展示,其余配置可参考SpringBoot源码配置。

如下是配置的bean的先后顺序:

在这里插入图片描述

测试使用的实体类JackSonDemo定义如下(暂时不使用自定义jackson注解):

packagecom.xiaoxu.test.jackson;importcom.fasterxml.jackson.annotation.JacksonAnnotationsInside;importcom.fasterxml.jackson.databind.annotation.JsonSerialize;importlombok.Data;importjava.lang.annotation.*;importjava.time.LocalDateTime;importjava.util.Date;/**
 * @author xiaoxu
 * @date 2022-12-30
 * java_demo:com.xiaoxu.test.jackson.JackSonDemo
 *///@Wrap@DatapublicclassJackSonDemo{long uid;String name;Date now;LocalDateTime localDay;}@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@JacksonAnnotationsInside@JsonSerialize(using =JackSonDemoSerializer.class)@interfaceWrap{}

JackSonDemoSub 实体类如下:

@DatapublicclassJackSonDemoSub{String name;JackSonDemo jackSonDemo;}

controller:

packagecom.xiaoxu.test.springBoot.controller;importcom.xiaoxu.test.jackson.JackSonDemo;importcom.xiaoxu.test.jackson.JackSonDemoSub;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;importjava.time.LocalDateTime;importjava.util.Date;/**
 * @author xiaoxu
 * @date 2022-12-30
 * java_demo:com.xiaoxu.test.springBoot.controller.TestJackSonController
 */@RequestMapping(value ="/jackson")@RestControllerpublicclassTestJackSonController{@RequestMapping(value ="/demo1")publicJackSonDemojackSonOne(@RequestParam(required =false)String name,@RequestParam(required =false)Integer uid){JackSonDemo jackSonDemo =newJackSonDemo();if(uid !=null){
            jackSonDemo.setUid(uid);}else{
            jackSonDemo.setUid(100);}if(name !=null){
            jackSonDemo.setName(name);}

        jackSonDemo.setNow(newDate());
        jackSonDemo.setLocalDay(LocalDateTime.now());return jackSonDemo;}@RequestMapping(value ="/demo2")publicJackSonDemoSubjackSonDemoSub(@RequestParam(required =false)String name){JackSonDemo jackSonDemo =newJackSonDemo();
        jackSonDemo.setUid(100);if(name !=null){
            jackSonDemo.setName(name);}
        jackSonDemo.setNow(newDate());
        jackSonDemo.setLocalDay(LocalDateTime.now());JackSonDemoSub jackSonDemoSub =newJackSonDemoSub();
        jackSonDemoSub.setJackSonDemo(jackSonDemo);
        jackSonDemoSub.setName("nihao");return jackSonDemoSub;}}

postman请求测试:

在这里插入图片描述
可见name为null时,并未展示name字段,可知使用的为自定义配置的jackson objectMapper.

增加name字段请求:

在这里插入图片描述
请求demo2接口,效果如下:

在这里插入图片描述


2.2 自定义Jackson ObjectMapper配置类,可注册自定义jackson的序列化、反序列化器:

自定义序列化器:

packagecom.xiaoxu.test.jackson;importcom.fasterxml.jackson.core.JsonGenerator;importcom.fasterxml.jackson.databind.BeanProperty;importcom.fasterxml.jackson.databind.JsonMappingException;importcom.fasterxml.jackson.databind.JsonSerializer;importcom.fasterxml.jackson.databind.SerializerProvider;importcom.fasterxml.jackson.databind.ser.ContextualSerializer;importjava.io.IOException;importjava.text.SimpleDateFormat;importjava.time.format.DateTimeFormatter;importjava.util.Optional;/**
 * @author xiaoxu
 * @date 2022-12-30
 * java_demo:com.xiaoxu.test.jackson.JackSonDemoSerializer
 */publicclassJackSonDemoSerializerextendsJsonSerializer<JackSonDemo>implementsContextualSerializer{@Overridepublicvoidserialize(JackSonDemo jackSonDemo,JsonGenerator jsonGenerator,SerializerProvider serializerProvider)throwsIOException{
            jsonGenerator.writeStartObject();
            jsonGenerator.writeStringField("RealName",jackSonDemo.getName());
            jsonGenerator.writeNumberField("userId",jackSonDemo.uid +1);SimpleDateFormat sdf =newSimpleDateFormat("yyyy-MM-dd HH:mm:ss");Optional.ofNullable(jackSonDemo.getNow()).ifPresent(date ->{try{String day = sdf.format(jackSonDemo.getNow());
                    jsonGenerator.writeStringField("nowDay",day);}catch(IOException e){thrownewRuntimeException("now转换异常:"+e.getMessage(),e.getCause());}});Optional.ofNullable(jackSonDemo.getLocalDay()).ifPresent(localDate ->{try{
                    jsonGenerator.writeStringField("localDay",
                            jackSonDemo.getLocalDay().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));}catch(IOException e){thrownewRuntimeException("todayNew转换异常:"+e.getMessage(),e.getCause());}});
            jsonGenerator.writeStringField("extra","over");
            jsonGenerator.writeEndObject();}@OverridepublicJsonSerializer<JackSonDemo>createContextual(SerializerProvider serializerProvider,BeanProperty beanProperty)throwsJsonMappingException{returnthis;}}

全局配置中,注册自定义序列化器:

@ConfigurationpublicclassJacksonGlobalConfig{@Bean(name ="jackson2ObjectMapperBuilder")/* 观察SpringBoot-2.5.4
    * jackson的自动配置类:JacksonAutoConfiguration 源码
    *  */@SuppressWarnings(value ="   deprecation ")publicJackson2ObjectMapperBuilderxiaoxuJackson2ObjectMapperBuilder(){System.out.println("1.jackson builder注入:");Jackson2ObjectMapperBuilder builder =newJackson2ObjectMapperBuilder();
        builder.serializationInclusion(JsonInclude.Include.NON_NULL);
        builder.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
        builder.featuresToEnable(SerializationFeature.WRAP_ROOT_VALUE);/* 针对new Date  SimpleDateFormat线程不安全, 建议使用自定义的日期工具类,
        * 此处只做简单演示配置*//* 注意: 使用 yyyy-MM-dd HH:mm:ss.fff 将报错 *//* 使用 dateFormat, new Date 和 LocalDateTime 均会产生对应效果*//* new Date 不设置的情况下,默认返回时间戳 */
        builder.dateFormat(newSimpleDateFormat("yyyy-MM-dd HH:mm:ss"));/* 全局注册自定义序列化器 */
        builder.serializerByType(JackSonDemo.class,newJackSonDemoSerializer());return builder;}@Bean(name ="jacksonObjectMapper")@Primary@ConditionalOnBean(name ={"jackson2ObjectMapperBuilder"})/* Jackson的ObjectMapper 是线程安全的, 不过SpringBoot2.5.4源码上使用的是非单例模式,这里和源码保持一致 */@Scope("prototype")//    @Scope("singleton")publicObjectMapperxiaoxuJacksonObjectMapper(@Qualifier("jackson2ObjectMapperBuilder")Jackson2ObjectMapperBuilder builder){System.out.println("2.jackson objectMapper注入:");return builder.createXmlMapper(false).build();}}

postman再次执行demo1请求,可见现在序列化时,使用的是自定义的JackSonDemoSerializer序列化器:

在这里插入图片描述

demo1接口增加name和uid再次请求:

在这里插入图片描述

同理,请求demo2效果如下:

在这里插入图片描述

在这里插入图片描述

或者直接在实体类上,使用自定义的jackson注解,此时可不需配置Jackson ObjectMapper全局序列化器:

在这里插入图片描述

实体类上使用自定义的jackson注解@Wrap,再次执行,效果和全局配置一致:

在这里插入图片描述


标签: spring boot spring java

本文转载自: https://blog.csdn.net/a232884c/article/details/128499245
版权归原作者 小徐也要努力鸭 所有, 如有侵权,请联系我们删除。

“spring boot 七:SpringBoot自定义配置Jackson的ObjectMapper”的评论:

还没有评论