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

news2025/2/6 20:47:20

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

本系列Jackson 版本 2.17.0

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

文章目录

    • 1. 前言
    • 2. Spring Web
    • 3. Jackson2ObjectMapperBuilder
    • 4. Jackson2ObjectMapperFactoryBean
    • 5. MappingJackson2HttpMessageConverter

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自身使用和推荐使用的方式。

它的构造方法是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();

此外还提供了很多方法,例如自定义序列化/反序列化器、模块注册、启用/禁用特征、 混合注解等等,和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 modules(Module... modules) {
		return modules(Arrays.asList(modules));
	}
	/**
	 * 启用特征
	 *
	 * @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;
	}

需要重点关注的方法有buildconfigurebuild方法用于构建一个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;
	}

configure方法用于将成员属性都设置给已存在的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);
		}
	}

4. Jackson2ObjectMapperFactoryBean

Jackson2ObjectMapperFactoryBean提供了基于Spring框架FactoryBean机制创建和配置ObjectMapper实例。

示例如下:

@Configuration
public class MvcConfig {

    @Bean
    public Jackson2ObjectMapperFactoryBean jacksonObjectMapper() {
        Jackson2ObjectMapperFactoryBean factoryBean = new Jackson2ObjectMapperFactoryBean();
        factoryBean.setIndentOutput(true);
        factoryBean.setSimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 还可以设置其他属性,如自定义的序列化器、反序列化器等
        return factoryBean;
    }
}

5. MappingJackson2HttpMessageConverter

MappingJackson2HttpMessageConverter即基于Jackson2的消息转换器实现,其本身并没有多少代码,读写能力来自于父类。

继承关系如下:

  • HttpMessageConverter
    • GenericHttpMessageConverter
      • AbstractGenericHttpMessageConverter
        • AbstractJackson2HttpMessageConverter
          • MappingJackson2HttpMessageConverter

顶级接口HttpMessageConverterSpring Web定义的一个HTTP消息转换器。负责将请求和响应的数据从Java对象转换为HTTP协议所需的格式,或者将HTTP协议中的数据转换为Java对象,是Spring框架中用于处理HTTP请求和响应数据转换的重要组件。

直接父类AbstractJackson2HttpMessageConverter定义了泛型为Object,其内部维护了多个ObjectMapper对象:

public abstract class AbstractJackson2HttpMessageConverter extends 
										AbstractGenericHttpMessageConverter<Object> 
	// 默认的ObjectMapper
    protected ObjectMapper defaultObjectMapper;
    @Nullable
    // registerObjectMappersForType 方法注册的多个ObjectMapper
    private Map<Class<?>, Map<MediaType, ObjectMapper>> objectMapperRegistrations;

可以调用构造方法或者set方法设置默认的ObjectMapper

    protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        this.defaultObjectMapper = objectMapper;
        DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
        prettyPrinter.indentObjectsWith(new DefaultIndenter("  ", "\ndata:"));
        this.ssePrettyPrinter = prettyPrinter;
    }

    protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper, MediaType supportedMediaType) {
        this(objectMapper);
        this.setSupportedMediaTypes(Collections.singletonList(supportedMediaType));
    }

    protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper, MediaType... supportedMediaTypes) {
        this(objectMapper);
        this.setSupportedMediaTypes(Arrays.asList(supportedMediaTypes));
    }
    
    public void setObjectMapper(ObjectMapper objectMapper) {
        Assert.notNull(objectMapper, "ObjectMapper must not be null");
        this.defaultObjectMapper = objectMapper;
        this.configurePrettyPrint();
    }

重写了父类的canReadcanWrite方法,例如canRead,首先调用父类的 canRead 方法,是否支持当前 MediaType,然后查询并判断ObjectMapper是否支持反序列化当前类型:

    public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
    	// 调用父类的 canRead 方法,是否支持当前 MediaType
        if (!this.canRead(mediaType)) {
            return false;
        } else {
        	// 根据JAVA Type 类型,转换为 Jackson 的 JavaType 
            JavaType javaType = this.getJavaType(type, contextClass);
            // 查询一个可用的 ObjectMapper 
            ObjectMapper objectMapper = this.selectObjectMapper(javaType.getRawClass(), mediaType);
            if (objectMapper == null) {
                return false;
            } else {
            	// ObjectMapper  是否可反序列化当前类型 
                AtomicReference<Throwable> causeRef = new AtomicReference();
                if (objectMapper.canDeserialize(javaType, causeRef)) {
                    return true;
                } else {
                    this.logWarningIfNecessary(javaType, (Throwable)causeRef.get());
                    return false;
                }
            }
        }
    }

最重要的是真正实现了转换器最核心的读写方法,例如read方法中并不是直接通过ObjectMapper进行读操作,而是通过ObjectMapper创建了底层的ObjectReader 去执行读操作。

	// 将 HTTP 输入消息,转换为 JAVA 对象
    public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        JavaType javaType = this.getJavaType(type, contextClass);
        return this.readJavaType(javaType, inputMessage);
    }
	
    private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
    	// 获取 Content-Type
        MediaType contentType = inputMessage.getHeaders().getContentType();
        // 获取 Charset 
        Charset charset = this.getCharset(contentType);
        // 查询可用的 ObjectMapper 
        ObjectMapper objectMapper = this.selectObjectMapper(javaType.getRawClass(), contentType);
        Assert.state(objectMapper != null, () -> {
            return "No ObjectMapper for " + javaType;
        });
        boolean isUnicode = ENCODINGS.containsKey(charset.name()) || "UTF-16".equals(charset.name()) || "UTF-32".equals(charset.name());

        try {
        	// 获取输入流 
            InputStream inputStream = StreamUtils.nonClosing(inputMessage.getBody());
            if (inputMessage instanceof MappingJacksonInputMessage) {
            	// 支持 Jackson 视图 
                MappingJacksonInputMessage mappingJacksonInputMessage = (MappingJacksonInputMessage)inputMessage;
                Class<?> deserializationView = mappingJacksonInputMessage.getDeserializationView();
                if (deserializationView != null) {
                    ObjectReader objectReader = objectMapper.readerWithView(deserializationView).forType(javaType);
                    objectReader = this.customizeReader(objectReader, javaType);
                    if (isUnicode) {
                        return objectReader.readValue(inputStream);
                    }

                    Reader reader = new InputStreamReader(inputStream, charset);
                    return objectReader.readValue(reader);
                }
            }
			// 使用 `ObjectMapper` 底层的`ObjectReader`读取
            ObjectReader objectReader = objectMapper.reader().forType(javaType);
            objectReader = this.customizeReader(objectReader, javaType);
            if (isUnicode) {
                return objectReader.readValue(inputStream);
            } else {
                Reader reader = new InputStreamReader(inputStream, charset);
                return objectReader.readValue(reader);
            }
        } catch (InvalidDefinitionException var12) {
            throw new HttpMessageConversionException("Type definition error: " + var12.getType(), var12);
        } catch (JsonProcessingException var13) {
            throw new HttpMessageNotReadableException("JSON parse error: " + var13.getOriginalMessage(), var13, inputMessage);
        }
    }

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

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

相关文章

STL —— priority_queue

博主首页&#xff1a; 有趣的中国人 专栏首页&#xff1a; C专栏 本篇文章主要讲解 priority_queue 的相关内容 目录 1. 优先级队列简介 基本操作 2. 模拟实现 2.1 入队操作 2.2 出队操作 2.3 访问队列顶部元素 2.4 判断优先队列是否为空 2.5 获取优先队列的大小 …

分布式向量数据库-安装部署

下载 GitHub - pgvector/pgvector: Open-source vector similarity search for Postgres 源码编译 ##文件解压缩 unzip pgvector-0.6.2.zip ##编译 make && make install 功能验证 #安装扩展CREATE EXTENSION vector;#创建测试表CREATE TABLE items (id bigseri…

mysql题目1

tj11: ​ select * from t_student where grade 大一 and major 软件工程 ​ tj12: SELECTt_student.name, count(t_choice.cid)FROMt_choiceINNER JOINt_courseON t_choice.cid t_course.idINNER JOINt_studentON t_choice.sid t_student.id GROUP BYt_choice.sid HAVIN…

【Unity 实用工具篇】 | UIEffect 实现一系列UGUI特效,灰度、负片、像素化特效

前言 【Unity 实用工具篇】 | UIEffect 实现一系列UGUI特效&#xff0c;灰度、负片、像素化特效一、UGUI特效插件&#xff1a;UIEffect1.1 介绍1.2 效果展示1.3 使用说明及下载 二、组件属性面板三、代码操作组件四、组件常用方法示例4.1 使用灰度特效做头像(关卡)选择 总结 前…

win11电脑驱动怎么更新,windows11更新驱动

驱动是指计算机里软件的程序,硬件的运作离不开驱动的支持,因为驱动就是使得硬件和电脑系统沟通的桥梁。既然驱动如此重要,那么不装肯定不行,如果有问题,也要及时地修复和更新。最近,有位win11用户,想要了解win11电脑驱动怎么更新?接下来,教程会带来两种更新win11驱动的…

CodeForce[1500-2000]——1948D Tandem Repeats?

大概题目意思就是&#xff1a;给你一个只有小写字母和问号的字符串&#xff0c;可以在头或尾删除任意长度&#xff0c;得到一个字串&#xff0c;并且该字串要满足长度为偶数&#xff0c;前一半和后一半&#xff08;问号可以匹配任意字符&#xff09;相等&#xff0c;求这样的字…

专治Java底子差,线程操作篇(2)

&#x1f497;推荐阅读文章&#x1f497; &#x1f338;JavaSE系列&#x1f338;&#x1f449;1️⃣《JavaSE系列教程》&#x1f33a;MySQL系列&#x1f33a;&#x1f449;2️⃣《MySQL系列教程》&#x1f340;JavaWeb系列&#x1f340;&#x1f449;3️⃣《JavaWeb系列教程》…

2024五一杯数学建模A题B题C题思路汇总分析

文章目录 1 赛题思路2 比赛日期和时间3 组织机构4 建模常见问题类型4.1 分类问题4.2 优化问题4.3 预测问题4.4 评价问题 5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 比赛日期和时间 报名截止时间&#xff1a;2024…

设计模式——2_9 模版方法(Template Method)

人们往往把任性也叫做自由&#xff0c;但是任性只是非理性的自由&#xff0c;人性的选择和自决都不是出于意志的理性&#xff0c;而是出于偶然的动机以及这种动机对感性外在世界的依赖 ——黑格尔 文章目录 定义图纸一个例子&#xff1a;从文件中获取信息分几步&#xff1f;Rea…

Hive概述与基本操作

一、Hive基本概念 1.什么是hive? &#xff08;1&#xff09;hive是数据仓库建模的工具之一 &#xff08;2&#xff09;可以向hive传入一条交互式的sql,在海量数据中查询分析得到结果的平台 2.Hive简介 Hive本质是将SQL转换为MapReduce的任务进行运算&#xff0c;底层由HDFS…

经典文献阅读之--A Survey on Generative Diffusion Models(扩散模型最新综述)

0. 简介 本文综述了深度生成模型&#xff0c;特别是扩散模型(Diffusion model)&#xff0c;如何赋予机器类似人类的想象力。扩散模型在生成逼真样本方面显示出巨大潜力&#xff0c;克服了变分自编码器中的后分布对齐障碍&#xff0c;缓解了生成对抗网络中的对抗性目标不稳定性…

深度挖掘响应式模式的潜力,从而精准优化AI与机器学习项目的运行效能,引领技术革新潮流

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f525; 转载自热榜文章&#xff1a;探索设计模式的魅力&#xff1a;深度挖掘响应式模式的…

uni-starter的微信登录拿不到登录者的昵称,头像,手机号问题记录

uni-starter的微信登录竟然拿不到登录者的昵称&#xff0c;头像&#xff0c;手机号 获取手机号的方法在另外一篇文章中&#xff0c;需要认证&#xff0c;需要有营业执照 uni.login({"provider": type,"onlyAuthorize": true,// #ifdef APP"univerif…

RabbitMQ - Spring boot 整合 RabbitMQ

一、RabbitMQ 1、RabbitMQ 使用场景 1.1、服务解耦 假设有这样一个场景, 服务A产生数据, 而服务B,C,D需要这些数据, 那么我们可以在A服务中直接调用B,C,D服务,把数据传递到下游服务即可 但是,随着我们的应用规模不断扩大,会有更多的服务需要A的数据,如果有几十甚至几百个下…

Docker Desktop修改镜像存储路径 Docker Desktop Start ... 卡死

1、CMD执行wsl -l -v --all 2、Clean / Purge data 3、导出wsl子系统镜像: wsl --export docker-desktop D:\docker\wsl\distro\docker-desktop.tar wsl --export docker-desktop-data D:\docker\wsl\data\docker-desktop-data.tar4、删除现有的wsl子系统&#xff1a; wsl -…

智游剪辑网页版发布了!

你是否因为软件安装麻烦而不愿意尝试本软件&#xff1f;那么可以试试网页版&#xff01;只需要一个浏览器&#xff0c;就可以直接访问了&#xff0c;网页版免安装&#xff0c;无广告&#xff0c;大部分功能都可以免费使用&#xff01; 网页版地址&#xff1a;app.zyjj.cc 界面…

汇编基础-----通过x64dbg了解什么是堆栈

汇编基础-----通过x64dbg了解什么是堆栈 什么是堆栈 在汇编语言中&#xff0c;堆栈&#xff08;stack&#xff09;是一种用于存储临时数据和执行函数调用的内存结构。堆栈是一种后进先出&#xff08;Last-In-First-Out, LIFO&#xff09;的数据结构&#xff0c;通常用于保存函…

ssm042在线云音乐系统的设计与实现+jsp

在线云音乐系统的设计与实现 摘 要 随着移动互联网时代的发展&#xff0c;网络的使用越来越普及&#xff0c;用户在获取和存储信息方面也会有激动人心的时刻。音乐也将慢慢融入人们的生活中。影响和改变我们的生活。随着当今各种流行音乐的流行&#xff0c;人们在日常生活中经…

08_ADC轮询方式读取电压值/DMA方式多通道采集/DAC数模转换

ADC轮询方式读取电压值/DMA方式多通道采集/DAC数模转换 ADC轮询方式读取电压值DMA方式多通道采集DAC数模转换 ADC轮询方式读取电压值 while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */HAL_ADC_Start(&hadc1);//启动ADC装换HAL_ADC_PollForConversion(&hadc…

多维时序 | Matlab实现TCN-LSTM时间卷积长短期记忆神经网络多变量时间序列预测

多维时序 | Matlab实现TCN-LSTM时间卷积长短期记忆神经网络多变量时间序列预测 目录 多维时序 | Matlab实现TCN-LSTM时间卷积长短期记忆神经网络多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.【Matlab实现TCN-LSTM时间卷积长短期记忆神经网络多变量…