Jackson 2.x 系列【24】Spring Web 集成之 Jackson2ObjectMapperBuilder

有道无术,术尚可求,有术无道,止于术。

本系列Jackson 版本 2.17.0

源码地址:https://gitee.com/pearl-organization/study-jaskson-demo

文章目录

    • 1. 前言
    • 2. Spring Web
    • 3. Jackson2ObjectMapperBuilder
      • 3.1 成员属性
      • 3.2 静态方法
      • 3.3 自定义序列化/反序列化器
      • 3.4 模块注册
      • 3.5 启用/禁用特征
      • 3.6 混合注解
      • 3.7 构建
      • 3.8 配置
      • 3.9 其他

1. 前言

Spring生态使用Jackson作为默认的JSON处理框架,这些年随着Spring的大发异彩和Jackson本身的优越特性,已成为世界上最流行的JSON库。

接下来,本系列会讲解Spring MVCSpring Boot中如何使用Jackson,以及Spring对其进行扩展增强的相关源码,所以需要读者有一定的SpringSpring Boot开发经验。

2. Spring Web

Spring MVC对于后端开发人员来说已经很熟悉了,它是一个构建在Servlet之上的一个Web框架,而Spring WebSpring MVC的基础模块(还有一个Spring Flux ),它们同属于spring-framework框架。

Web框架在处理HTTP请求响应时,需要将请求报文反序列化为Java对象进行接收处理,在响应阶段,需要将Java对象反序列化为报文写出,所以需要依赖JSON框架去处理。

Spring Web中可以看到引入了JacksonGJson

在这里插入图片描述
spring-web模块的org.springframework.http.converter.json包下,可以看到Json集成的代码:

在这里插入图片描述

集成Jackson的主要有:

  • Jackson2ObjectMapperBuilderObjectMapper构建器
  • Jackson2ObjectMapperFactoryBeanFactoryBean创建和管理ObjectMapper对象
  • MappingJackson2HttpMessageConverter:基于Jackson的消息转换器
  • SpringHandlerInstantiatorSpring容器创建Jackson组件,例如JsonSerializerJsonDeserializerKeyDeserializer
  • JacksonModulesRuntimeHintsSpring AOT所需的RuntimeHints(运行时提示)

3. Jackson2ObjectMapperBuilder

Jackson2ObjectMapperBuilder从名字上看已经很好理解,它是一个JacksonObjectMapper构建器,提供了Fluent API来定制ObjectMapper的默认属性并构建实例。

在之前我们都是通过new的方式创建ObjectMapper实例,现在可以使用构建者模式,这也是Spring自身使用和推荐使用的方式。

3.1 成员属性

	// 是否引入了`jackson-dataformat-xml`模块
	private static final boolean jackson2XmlPresent = ClassUtils.isPresent(
			"com.fasterxml.jackson.dataformat.xml.XmlMapper", Jackson2ObjectMapperBuilder.class.getClassLoader());
	// 混合注解类
	private final Map<Class<?>, Class<?>> mixIns = new LinkedHashMap<>();
	// 序列化器
	private final Map<Class<?>, JsonSerializer<?>> serializers = new LinkedHashMap<>();
	// 反序列化器
	private final Map<Class<?>, JsonDeserializer<?>> deserializers = new LinkedHashMap<>();

	private final Map<PropertyAccessor, JsonAutoDetect.Visibility> visibilities = new LinkedHashMap<>();
	// 特征
	private final Map<Object, Boolean> features = new LinkedHashMap<>();
	// 是否创建`XmlMapper`
	private boolean createXmlMapper = false;
	// 模块
	@Nullable
	private List<Module> modules;
	// JsonFactory
	@Nullable
	private JsonFactory factory;
	// 省略.........

3.2 静态方法

它的构造方法是public的,说明可以直接new创建Jackson2ObjectMapperBuilder

    public Jackson2ObjectMapperBuilder() {
    }

提供了多个创建不同数据类型支持的静态方法:

	// 构建 Jackson2ObjectMapperBuilder
	public static Jackson2ObjectMapperBuilder json() {
		return new Jackson2ObjectMapperBuilder();
	}

	// 构建 Jackson2ObjectMapperBuilder,并设置需要创建 XmlMapper,需要引入`jackson-dataformat-xml`模块,用于支持`XML`数据格式
	public static Jackson2ObjectMapperBuilder xml() {
		return new Jackson2ObjectMapperBuilder().createXmlMapper(true);
	}

	// 指定 JsonFactory-》SmileFactory,需要引入`jackson-dataformat-smile`模块,用于支持`Smile`数据格式
	public static Jackson2ObjectMapperBuilder smile() {
		return new Jackson2ObjectMapperBuilder().factory(new SmileFactoryInitializer().create());
	}
	
	// 指定 JsonFactory-》CBORFactory-》需要引入`jackson-dataformat-cbor`模块,用于支持`CBOR`数据格式
	public static Jackson2ObjectMapperBuilder cbor() {
		return new Jackson2ObjectMapperBuilder().factory(new CborFactoryInitializer().create());
	}

一般场景中,使用json()创建即可:

ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();

3.3 自定义序列化/反序列化器

提供了多个方法用于配置自定义序列化/反序列化器,相较于ObjectMapper不能直接添加来说,方便了很多。

	// 配置自定义序列化器(多个)
	public Jackson2ObjectMapperBuilder serializers(JsonSerializer<?>... serializers) {
		for (JsonSerializer<?> serializer : serializers) {
			Class<?> handledType = serializer.handledType();
			if (handledType == null || handledType == Object.class) {
				throw new IllegalArgumentException("Unknown handled type in " + serializer.getClass().getName());
			}
			this.serializers.put(serializer.handledType(), serializer);
		}
		return this;
	}

	// 为给定的类型配置自定义的序列化器(单个)
	public Jackson2ObjectMapperBuilder serializerByType(Class<?> type, JsonSerializer<?> serializer) {
		this.serializers.put(type, serializer);
		return this;
	}

	// 通过Map给定的类型配置自定义的序列化器(多个)
	public Jackson2ObjectMapperBuilder serializersByType(Map<Class<?>, JsonSerializer<?>> serializers) {
		this.serializers.putAll(serializers);
		return this;
	}
	
	// 添加自定义反序列化器
	public Jackson2ObjectMapperBuilder deserializers(JsonDeserializer<?>... deserializers) {
		// 省略.......
		return this;
	}
	
	// 省略其他类似.......

3.4 模块注册

提供了多个方法用于进行模块注册:

	public Jackson2ObjectMapperBuilder modules(Module... modules) {
		return modules(Arrays.asList(modules));
	}

	public Jackson2ObjectMapperBuilder modules(List<Module> modules) {
		// 省略......
		return this;
	}

	public Jackson2ObjectMapperBuilder modules(Consumer<List<Module>> consumer) {
		// 省略......
		return this;
	}

	public Jackson2ObjectMapperBuilder modulesToInstall(Module... modules) {
		// 省略......
		return this;
	}

	// 省略其他类似......

3.5 启用/禁用特征

提供了启用、禁用特征的相关方法:

	/**
	 * 启用特征
	 *
	 * @see com.fasterxml.jackson.core.JsonParser.Feature
	 * @see com.fasterxml.jackson.core.JsonGenerator.Feature
	 * @see com.fasterxml.jackson.databind.SerializationFeature
	 * @see com.fasterxml.jackson.databind.DeserializationFeature
	 * @see com.fasterxml.jackson.databind.MapperFeature
	 */
	public Jackson2ObjectMapperBuilder featuresToEnable(Object... featuresToEnable) {
		for (Object feature : featuresToEnable) {
			this.features.put(feature, Boolean.TRUE);
		}
		return this;
	}

	/**
	 * 禁用特征
	 *
	 * @see com.fasterxml.jackson.core.JsonParser.Feature
	 * @see com.fasterxml.jackson.core.JsonGenerator.Feature
	 * @see com.fasterxml.jackson.databind.SerializationFeature
	 * @see com.fasterxml.jackson.databind.DeserializationFeature
	 * @see com.fasterxml.jackson.databind.MapperFeature
	 */
	public Jackson2ObjectMapperBuilder featuresToDisable(Object... featuresToDisable) {
		for (Object feature : featuresToDisable) {
			this.features.put(feature, Boolean.FALSE);
		}
		return this;
	}

提供了直接配置一些特殊特征的开启/禁用方法:

	// MapperFeature#AUTO_DETECT_FIELDS
	public Jackson2ObjectMapperBuilder autoDetectFields(boolean autoDetectFields) {
		this.features.put(MapperFeature.AUTO_DETECT_FIELDS, autoDetectFields);
		return this;
	}

	// MapperFeature.AUTO_DETECT_GETTERS
	// MapperFeature.AUTO_DETECT_SETTERS
	// MapperFeature.AUTO_DETECT_IS_GETTERS
	public Jackson2ObjectMapperBuilder autoDetectGettersSetters(boolean autoDetectGettersSetters) {
		this.features.put(MapperFeature.AUTO_DETECT_GETTERS, autoDetectGettersSetters);
		this.features.put(MapperFeature.AUTO_DETECT_SETTERS, autoDetectGettersSetters);
		this.features.put(MapperFeature.AUTO_DETECT_IS_GETTERS, autoDetectGettersSetters);
		return this;
	}

	// MapperFeature#DEFAULT_VIEW_INCLUSION
	public Jackson2ObjectMapperBuilder defaultViewInclusion(boolean defaultViewInclusion) {
		this.features.put(MapperFeature.DEFAULT_VIEW_INCLUSION, defaultViewInclusion);
		return this;
	}

	// DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES
	public Jackson2ObjectMapperBuilder failOnUnknownProperties(boolean failOnUnknownProperties) {
		this.features.put(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties);
		return this;
	}

	// SerializationFeature#FAIL_ON_EMPTY_BEANS
	public Jackson2ObjectMapperBuilder failOnEmptyBeans(boolean failOnEmptyBeans) {
		this.features.put(SerializationFeature.FAIL_ON_EMPTY_BEANS, failOnEmptyBeans);
		return this;
	}

	// SerializationFeature#INDENT_OUTPUT
	public Jackson2ObjectMapperBuilder indentOutput(boolean indentOutput) {
		this.features.put(SerializationFeature.INDENT_OUTPUT, indentOutput);
		return this;
	}

3.6 混合注解

提供了直接添加混合注解类(覆盖某个类的注解)的相关方法:

	public Jackson2ObjectMapperBuilder mixIn(Class<?> target, Class<?> mixinSource) {
		this.mixIns.put(target, mixinSource);
		return this;
	}

	public Jackson2ObjectMapperBuilder mixIns(Map<Class<?>, Class<?>> mixIns) {
		this.mixIns.putAll(mixIns);
		return this;
	}

3.7 构建

用于构建一个ObjectMapper实例,每次调用都会返回一个新的对象,对于处理不同JSON格式或需要不同序列化/反序列化行为的场景,可以构建新的实例来处理。

	@SuppressWarnings("unchecked")
	public <T extends ObjectMapper> T build() {
		// 创建实例
		ObjectMapper mapper;
		if (this.createXmlMapper) {
			// 需要创建XmlMapper,则使用 XmlFactory
			mapper = (this.defaultUseWrapper != null ?
					new XmlObjectMapperInitializer().create(this.defaultUseWrapper, this.factory) :
					new XmlObjectMapperInitializer().create(this.factory));
		} else {
			// 不需要创建XmlMapper,则使用指定的工厂
			mapper = (this.factory != null ? new ObjectMapper(this.factory) : new ObjectMapper());
		}
		// 配置
		configure(mapper);
		return (T) mapper;
	}

3.8 配置

用于将成员属性都设置给已存在的ObjectMapper实例:

	public void configure(ObjectMapper objectMapper) {
		Assert.notNull(objectMapper, "ObjectMapper must not be null");
		// 注册模块
		MultiValueMap<Object, Module> modulesToRegister = new LinkedMultiValueMap<>();
		if (this.findModulesViaServiceLoader) {
			ObjectMapper.findModules(this.moduleClassLoader).forEach(module -> registerModule(module, modulesToRegister));
		} else if (this.findWellKnownModules : modulesToRegister.values()) {
			modules.addAll(nestedModules);
		}
		objectMapper.registerModules(modules);
		// 设置时间日期格式化
		if (this.dateFormat != null) {
			objectMapper.setDateFormat(this.dateFormat);
		}
		// 设置 Locale
		if (this.locale != null) {
			objectMapper.setLocale(this.locale);
		}
		// 设置 TimeZone	
		if (this.timeZone != null) {
			objectMapper.setTimeZone(this.timeZone);
		}
		// 设置 注解解析器
		if (this.annotationIntrospector != null) {
			objectMapper.setAnnotationIntrospector(this.annotationIntrospector);
		}
		// 设置属性名称策略
		if (this.propertyNamingStrategy != null) {
			objectMapper.setPropertyNamingStrategy(this.propertyNamingStrategy);
		}
		// 省略其他.........
		if (this.configurer != null) {
			this.configurer.accept(objectMapper);
		}
	}

3.9 其他

此外还提供了其他的方法,使用率较低,就不在一一讲解了。

	// 是否创建  XmlMapper
	public Jackson2ObjectMapperBuilder createXmlMapper(boolean createXmlMapper) {
		this.createXmlMapper = createXmlMapper;
		return this;
	}

	// 设置 JsonFactory
	public Jackson2ObjectMapperBuilder factory(JsonFactory factory) {
		this.factory = factory;
		return this;
	}


	// 设置 DateFormat
	public Jackson2ObjectMapperBuilder dateFormat(DateFormat dateFormat) {
		this.dateFormat = dateFormat;
		return this;
	}
	
	// 省略其他..........

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FMEA分析

目录 1、FMEA的核心目的 2、FMEA的种类 3、FMEA的实施步骤 4、FMEA的SOD等级 5、FMEA的例子 FMEA&#xff08;Failure Modes and Effects Analysis&#xff0c;失效模式与影响分析&#xff09;是一种预防性的可靠性设计分析&#xff0c;用来确定潜在失效模式及其原因。它主…

IDEA使用SCALA

一、在IDEA中下载插件 在设置->插件中找到scala&#xff0c;并下载。 下载完成后重启idea 二、在idea中创建spark的RDD操作项目 新建项目选中Scala。 创建完成后为项目添加java包&#xff0c;这个添加的是spark安装包中jars目录下的所有jar包 然后编写RDD操作 import or…

安全中级-初开始

一、网络基础 重要点&#xff1a;TTL值&#xff08;防环&#xff0c;linux64.Windows128 &#xff09;&#xff0c;IP数据包包头格式字节&#xff08;20&#xff09; 标识标志偏移量起到什么作用&#xff08;数据超过1500会分片&#xff09; wireshack抓包会有一个MSS&#x…

铭飞 MCMS 存在SQL注入漏洞

声明&#xff1a; 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 简介 铭飞&#xff08;MCMS&#xff09;是一种计算机管理…

Opencv3.4+FFMpeg3.4+pkg-config交叉编译arm开发板

Ubuntu16.04 64位 FFmpeg3.4 OpenCv3.4 一、下载FFmpeg https://github.com/FFmpeg/FFmpeg 1.配置 ./configure --prefix/home/zeng/ffmpeg_install --enable-cross-compile --cross-prefixarm-linux-gnueabihf- --ccarm-linux-gnueabihf-gcc --target-oslinux --cpuco…

算法课程笔记——排序

Bool返回真假 为何用const不用define 1.保护被修饰的东西 2.通常不分配存储空间&#xff0c; 效率高 匿名函数只在一处用&#xff0c;其他处用不到 不写&就是拷贝 u相等就u&#xff0c;不等就v 一个字符是空格一个是换行&#xff0c;后面是取下标i那就是1&#xff08;true&…

图解数学:拉格朗日松弛方法的直观理解

昨晚写了拉格朗日松弛方法的原理分析&#xff0c;今天意犹未尽&#xff0c;图解一下&#xff0c;从直观上进一步理解这种方法。 一、一个简单例子 我们先来看一个简单的例子&#xff0c;下面数学规划问题没有约束条件&#xff1a; min ⁡ f ( x ) − x 2 8 x − 10 \begin…

全排列问题

日升时奋斗&#xff0c;日落时自省 目录 1、全排列 2、全排列II 3、子集 4、组合 1、全排列 首先要了解全排列是怎么样的 例如:数组[1,2,3]的全排列&#xff08;全排列就是不同顺序排列方式&#xff09; 例子所有的排列方式如&#xff1a;[1,2,3],[1,3,2],[2,1,3],[2,3…

Leetcode876_链表的中间结点

1.leetcode原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 2.题目描述 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5…

PTA 编程题(C语言)-- 判断素数

题目标题&#xff1a; 判断素数 题目作者 陈越 浙江大学 本题的目标很简单&#xff0c;就是判断一个给定的正整数是否素数。 输入格式&#xff1a; 输入在第一行给出一个正整数N&#xff08;≤ 10&#xff09;&#xff0c;随后N行&#xff0c;每行给出一个小于…

康耐视visionpro-CoglntersectLineLineTool操作说明工具详细说明

◆CogIntersectLineLineTool功能说明&#xff1a; 创建两条线的交点 备注&#xff1a;在“Geometry-Intersection”选项中的所有工具都是创建两个图形的交点工具&#xff0c;其中包括圆与圆的交点、线与圆的交点、线与线的交点、线与圆的交点等&#xff0c;工具使用的方法相似。…

C++ - set 和 map详解

目录 0. 引言 1. 关联式容器 2. 键值对 3. 树形结构 4. set 4.1 set 的定义 4.2 set 的构造 4.3 set 的常用函数 4.4 set 的特点 5. multiset 5.1 multiset 插入冗余数据 5.2 multiset - count 的使用 6. map 6.1 map 的定义 6.2 map 的构造 6.3 map的常…

dcoker+nginx解决前端本地开发跨域

步骤 docker 拉取nginx镜像跑容器 并配置数据卷nginx.conf nginx.conf文件配置 这里展示server server {listen 80;listen [::]:80;server_name localhost;#access_log /var/log/nginx/host.access.log main;location / {# 当我们访问127.0.0.1:8028就会跳转到ht…

软件2班20240415

快速引用类选择器 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevi…

二进制离散无记忆对称信道

目录 引言 二进制离散无记忆对称信道 引言 在无线通信中&#xff0c;信源与信宿是分开的&#xff0c;两者间的通信环境的复杂度通常会随着距离 的增加而提升。信道是用来连接信源与信宿的媒介。信源与信宿之间的信息传递则需要 借助于信道&#xff0c;因此信道质量的优劣通常…

移动端web适配方案

以下是移动端适配的多个方案&#xff0c;也可以说说你是怎么做的。 正文 自适应&#xff1a;根据不同的设备屏幕大小来自动调整尺寸、大小 响应式&#xff1a;会随着屏幕的实时变动而自动调整&#xff0c;是一种更强的自适应 为什么要做移动端适配&#xff1f; 目前市面上…

【深度剖析】曾经让人无法理解的事件循环,前端学习路线

先自我介绍一下&#xff0c;小编浙江大学毕业&#xff0c;去过华为、字节跳动等大厂&#xff0c;目前阿里P7 深知大多数程序员&#xff0c;想要提升技能&#xff0c;往往是自己摸索成长&#xff0c;但自己不成体系的自学效果低效又漫长&#xff0c;而且极易碰到天花板技术停滞…

Redis入门到通关之ZSet命令

文章目录 ⛄概述⛄常见命令有⛄RedisTemplate API❄️❄️ 向集合中插入元素&#xff0c;并设置分数❄️❄️向集合中插入多个元素,并设置分数❄️❄️按照排名先后(从小到大)打印指定区间内的元素, -1为打印全部❄️❄️获得指定元素的分数❄️❄️返回集合内的成员个数❄️❄…

最优算法100例之49-链表环-计算环的长度

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 定义两个指针,一个指针走两步,一个指针走一步,第一次相遇时开始计数,第二次相遇时停止计数,最后计数器的值就是环的长度…

STM32H7上实现AD5758驱动

目录 概述 1 下载ADI 5758 Demo 2 AD5758驱动的移植 2.1 使用STM32CubeMX创建工程 2.2 接口函数实现 2.2.1 驱动接口列表 2.2.2 函数实现 2.2.3 修正ad5758驱动 3 AD5758应用程序 3.1 编写测试程序 3.1.1 配置参数结构 3.1.2 配置参数函数 3.1.3 读取参数函数 3.…