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

news2024/9/24 11:25:06

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时,均是重新生成的实例对象:

package com.xiaoxu.test.springBoot.configure;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.text.SimpleDateFormat;


/**
 * @author xiaoxu
 * @date 2022-12-30
 * java_demo:com.xiaoxu.test.springBoot.configure.JacksonGlobalConfig
 */
@Configuration
public class JacksonGlobalConfig {

    @Bean(name = "jackson2ObjectMapperBuilder")
    /* 观察SpringBoot-2.5.4
    * jackson的自动配置类:JacksonAutoConfiguration 源码
    *  */
    @SuppressWarnings(value = "   deprecation ")
    public Jackson2ObjectMapperBuilder xiaoxuJackson2ObjectMapperBuilder(){
        System.out.println("1.jackson builder注入:");
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        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(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        return builder;
    }

    @Bean(name = "jacksonObjectMapper")
    @Primary
    @ConditionalOnBean(name = {"jackson2ObjectMapperBuilder"})
    /* Jackson的ObjectMapper 是线程安全的, 不过SpringBoot2.5.4源码上使用的是非单例模式,这里和源码保持一致 */
    @Scope("prototype")
//    @Scope("singleton")
    public ObjectMapper xiaoxuJacksonObjectMapper(@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注解):

package com.xiaoxu.test.jackson;

import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;

import java.lang.annotation.*;
import java.time.LocalDateTime;
import java.util.Date;


/**
 * @author xiaoxu
 * @date 2022-12-30
 * java_demo:com.xiaoxu.test.jackson.JackSonDemo
 */
//@Wrap
@Data
public class JackSonDemo {
    long uid;
    String name;
    Date now;
    LocalDateTime localDay;
}

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@JacksonAnnotationsInside
@JsonSerialize(using = JackSonDemoSerializer.class)
@interface Wrap{}

JackSonDemoSub 实体类如下:

@Data
public class JackSonDemoSub {
    String name;
    JackSonDemo jackSonDemo;
}

controller:

package com.xiaoxu.test.springBoot.controller;

import com.xiaoxu.test.jackson.JackSonDemo;
import com.xiaoxu.test.jackson.JackSonDemoSub;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.Date;

/**
 * @author xiaoxu
 * @date 2022-12-30
 * java_demo:com.xiaoxu.test.springBoot.controller.TestJackSonController
 */
@RequestMapping(value = "/jackson")
@RestController
public class TestJackSonController {

    @RequestMapping(value = "/demo1")
    public JackSonDemo jackSonOne(@RequestParam(required = false) String name, @RequestParam(required = false) Integer uid){
        JackSonDemo jackSonDemo = new JackSonDemo();
        if(uid != null){
            jackSonDemo.setUid(uid);
        }else{
            jackSonDemo.setUid(100);
        }

        if(name != null){
            jackSonDemo.setName(name);
        }

        jackSonDemo.setNow(new Date());
        jackSonDemo.setLocalDay(LocalDateTime.now());
        return jackSonDemo;
    }

    @RequestMapping(value = "/demo2")
    public JackSonDemoSub jackSonDemoSub(@RequestParam(required = false) String name){
        JackSonDemo jackSonDemo = new JackSonDemo();
        jackSonDemo.setUid(100);
        if(name != null){
            jackSonDemo.setName(name);
        }
        jackSonDemo.setNow(new Date());
        jackSonDemo.setLocalDay(LocalDateTime.now());

        JackSonDemoSub jackSonDemoSub = new JackSonDemoSub();
        jackSonDemoSub.setJackSonDemo(jackSonDemo);
        jackSonDemoSub.setName("nihao");

        return jackSonDemoSub;
    }
}

postman请求测试:

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

增加name字段请求:

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

在这里插入图片描述


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

自定义序列化器:

package com.xiaoxu.test.jackson;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.Optional;

/**
 * @author xiaoxu
 * @date 2022-12-30
 * java_demo:com.xiaoxu.test.jackson.JackSonDemoSerializer
 */
public class JackSonDemoSerializer extends JsonSerializer<JackSonDemo> implements ContextualSerializer {

        @Override
        public void serialize(JackSonDemo jackSonDemo, JsonGenerator jsonGenerator,
                              SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeStartObject();
            jsonGenerator.writeStringField("RealName",jackSonDemo.getName());
            jsonGenerator.writeNumberField("userId",jackSonDemo.uid + 1);
            SimpleDateFormat sdf = new SimpleDateFormat("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) {
                    throw new RuntimeException("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) {
                    throw new RuntimeException("todayNew转换异常:"+e.getMessage(),e.getCause());
                }
            });
            jsonGenerator.writeStringField("extra", "over");
            jsonGenerator.writeEndObject();
        }

        @Override
        public JsonSerializer<JackSonDemo> createContextual(SerializerProvider serializerProvider,
                                                            BeanProperty beanProperty) throws JsonMappingException {
            return this;
        }
}

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

@Configuration
public class JacksonGlobalConfig {

    @Bean(name = "jackson2ObjectMapperBuilder")
    /* 观察SpringBoot-2.5.4
    * jackson的自动配置类:JacksonAutoConfiguration 源码
    *  */
    @SuppressWarnings(value = "   deprecation ")
    public Jackson2ObjectMapperBuilder xiaoxuJackson2ObjectMapperBuilder(){
        System.out.println("1.jackson builder注入:");
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        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(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        /* 全局注册自定义序列化器 */
        builder.serializerByType(JackSonDemo.class, new JackSonDemoSerializer());
        return builder;
    }

    @Bean(name = "jacksonObjectMapper")
    @Primary
    @ConditionalOnBean(name = {"jackson2ObjectMapperBuilder"})
    /* Jackson的ObjectMapper 是线程安全的, 不过SpringBoot2.5.4源码上使用的是非单例模式,这里和源码保持一致 */
    @Scope("prototype")
//    @Scope("singleton")
    public ObjectMapper xiaoxuJacksonObjectMapper(@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,再次执行,效果和全局配置一致:

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/137522.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[数据结构基础]栈和队列的结构及接口函数

一. 栈 1.1 栈的概念及结构 栈是一种特殊的线性表&#xff0c;其只允许在固定的一段进行插入和删除元素的操作。进行数据插入和删除的一端成为栈顶&#xff0c;另一端称为栈底。栈结构中的数据遵循先进后出原则&#xff08;LIFO&#xff1a;Last in First Out&#xff09;&am…

03 Hive概述

Hive概述1、什么是Hive2、Hive优缺点3、Hive架构原理4、Hive 和 数据库比较5、Hive计算引擎1、什么是Hive 由Facebook开源用于解决海量结构化日志的数据统计工具。 Hive是基于Hadoop的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张表&#xff0c;并提供 类SQ…

如何在外籍人员子女学校和同事有效工作

香港 (Xinwengao.com) — PD Academia 的 Henry Wong 在新加坡出生长大&#xff0c;在美国接受高等教育&#xff0c;现在在美国半退休。他在中国生活了16年。他与许多西方人和中国人一起工作。他始终将自己视为东方与西方的交汇点&#xff0c;是学术界与行政部门之间的桥梁。他…

刷油漆【中等】

在一个8 \times 8 的网格中&#xff0c;一些水平的行被涂成红色&#xff0c;一些垂直的列被涂成蓝色&#xff0c;条纹是按顺序画的&#xff0c;一个接着一个。绘制条纹时它会重新绘制它所经过的所有单元格。(原理跟刷油漆一样)现在你需要确定最后一个上色的是什么颜色红色条纹是…

静态路由配置

获取未知网段的路由信息&#xff1a; 静态路由&#xff1a;由网络管理员手工配置的路由条目 动态路由&#xff1a;所有路由器上开启同一种路由协议&#xff0c;之后&#xff0c;通过路由器之间沟通&#xff0c;协商&#xff0c;最终计算生成路由条目。 静态路由的优点&#xf…

MySQL2-初识MySQL

目录 1.数据库 1.1.什么是数据库? 1.2.为什么要用数据库&#xff1f; 1.2.1.数据存储的发展史 1.2.2.文件存储的缺点 1.3.数据库分类 1.3.1.关系型数据库&#xff08;RDBMS&#xff09; 1.3.2.非关系型数据库&#xff08;了解&#xff09; 1.3.3.关系型数据库和非关系…

STM32——TIM简介与TIM中断

文章目录一、TIM简介二、定时器类型基本定时器通用定时器高级定时器三、定时中断基本结构四、时序图预分频器时序计数器时序计数器无预装时序计数器有预装时序RCC时钟树五、定时器定时中断六、定时器外部时钟七、定时器库函数&#xff08;tim.h&#xff09;一、TIM简介 TIM&am…

【设计模式篇】设计模式3类23种

创建型模式 创建型模式提供了创建对象的机制&#xff0c; 能够提升已有代码的灵活性和可复用性。 这里面比较重要和经常用到时单例模式、工厂模式和原型模式。 单例模式 单例模式是一种创建型设计模式&#xff0c; 让你能够保证一个类只有一个实例&#xff0c; 并提供一个访问…

antd 动态增减表单项的使用

需求 首先这是基于antd的Form组件&#xff0c;需求1&#xff1a; 单选按钮组 选择设置时间 展示时间选择器需求2&#xff1a; 动态添加时间选择器(最多添加10个、时间为空校验、时间段重叠校验)需求3&#xff1a; 开关需求4&#xff1a;编辑时赋值 1. 单选钮组 <Radio.Gro…

4.6.1、路由选择协议概述

1、静态/动态路由 2、主要特点 因特网所采用的路由选择协议的主要特点 例如&#xff1a;一个较大的因特网提供商就可划分为一个自治系统 在自治系统内部和外部采用不同类别的路由选择协议&#xff0c;分别进行路由选择 3、分层次的路由选择协议 如下所示&#xff0c;将网络…

ArcGIS基础实验操作100例--实验44融合细碎多边形

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验44 融合细碎多边形 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&am…

k8s基础

一、基本介绍 Kubernetes&#xff0c;简称K8s&#xff0c;是用8代替8个字符“ubernete”而成的缩写。是一个开源的&#xff0c;用于管理云平台中多个主机上的容器化的应用&#xff0c;Kubernetes的目标是让部署容器化的应用简单并且高效(powerful) , Kubernetes提供了应用部署…

Linux 下 python3.9.8的安装

1. 准备安装环境 yum install gcc zlib* openssl* -y 2. linux 下 python 安装包的获取 官网下载地址: https://www.python.org/downloads/ 找到 自己想要的版本。 方式一&#xff1a;直接点击该链接下载&#xff0c;然后将安装包通过xftp 传送到远程服务器 的 /opt 路…

k8s之搭建单机集群

写在前面 本文一起看下如何在单机环境下搭建k8s集群。 基础环境准备参考这篇文章 。 1&#xff1a;k8s的前世今生 现在当我们提到容器技术时&#xff0c;首先想到的肯定是docker&#xff0c;但其实在docker之前&#xff0c;谷歌公司就已经有了类似的技术&#xff0c;我们知道…

借助免费AI艺术平台生成头像

随着 AI 的兴起&#xff0c;看到越来越多的实例通过 OpenAI 的举措变得轻松&#xff0c;使得 AI 艺术在今天早已不是什么新鲜事物&#xff0c;而且在游戏领域也开始有所应用。人工智能&#xff08;AI&#xff0c;artificial intelligence&#xff09;艺术&#xff0c;更准确地说…

数据挖掘与机器学习作业_09 贝叶斯

贝叶斯 贝叶斯公式 后验概率 先验概率 * 似然估计 from sklearn.model_selection import GridSearchCV from sklearn.naive_bayes import BernoulliNB from sklearn.naive_bayes import GaussianNB from sklearn.naive_bayes import MultinomialNB from sklearn.naive_bayes…

关于进程间的通信方式的总结

一、背景 在人类思想史上,马克思第一次对人的本质作出科学界定:人的本质是一切社会关系的总和。时间万物都存在或多或少的关系。那么人除了天生父子这样的家族关系&#xff0c;还有后天 通过 语言 &#xff0c;这样区别于其他动物的方式来进行和其他人的交流产生关系。 在计算…

PTL仓库提货解决方案

电子标签拣货系统是采用先进电子技术和通信技术开发而成的物流辅助作业系统&#xff0c;通常使用在仓储或现代化物流中心分拣环节&#xff0c;具有拣货速度快、效率高、差错率低、无纸化、标准化的作业特点&#xff0c;电子标签辅助拣货系统作为一种先进的作业手段&#xff0c;…

【小程序】如何开发属于自己的一款小程序

文章目录小程序简介概念小程序与普通网页开发的区别微信开发者工具小程序代码构成项目结构JSON 配置文件WXML 模板WXSS 样式JS 逻辑交互小程序的宿主环境宿主环境简介通信模型运行机制组件常用的视图容器类组件常用的基础内容组件其它常用组件API协同工作小程序成员管理小程序的…

数据完整性测试之【三】Redis缓存和数据库表里的记录

本文为博主原创&#xff0c;未经授权&#xff0c;严禁转载及使用。 本文链接&#xff1a;https://blog.csdn.net/zyooooxie/article/details/119377944 前面分享过 接口返回值 和 表记录 的校验 、 导出的CSV、Excel文件 和 表记录 的校验&#xff0c;最近 我们项目常常用到Re…