Jackson与GSON的深度对比

大家好,我是 V 哥。Jackson和Gson是Java中最常用的两个JSON解析库,它们在解析速度、灵活性、序列化/反序列化能力上各有特点。下面V 哥从功能特性、性能、源码实现等方面对比它们的优缺点。

V 哥推荐:2024 最适合入门的 JAVA 课程

1. 功能特性对比

  • Jackson

    • 提供全面的JSON处理支持,包括对象-JSON转换、树模型处理、流式处理等。
    • 具有广泛的注解支持,方便进行序列化/反序列化控制(如@JsonProperty@JsonIgnore等)。
    • 支持多种数据格式,包括XML、CSV、YAML等。
    • 具备较强的扩展能力,通过模块化架构可以扩展额外功能。
  • Gson

    • Gson提供简单、易用的API和注解(如@SerializedName)。
    • 在对象转换中可以自动处理空值,且支持Java泛型的序列化与反序列化。
    • Gson原生支持Java对象的嵌套映射,且支持通过TypeToken来实现复杂类型的反序列化。
    • 库文件轻量且易于集成。

2. 性能对比

一般来说,Jackson的性能优于Gson,尤其是在处理大数据量和复杂对象时,Jackson表现更佳。这主要归因于Jackson内部的高效流式解析器和缓存机制,它避免了内存中的大量临时对象创建,提升了处理速度。

Jackson的流式API(如JsonParserJsonGenerator)提供了细粒度的数据处理,适合在性能要求较高的场景使用。Gson则使用内存树模型处理JSON,这在内存开销和解析速度上较劣势。

3. 源码实现分析

接下来,V 哥通过分析两个组件的核心源码实现,来看一下两者的不同之处。

Jackson 源码实现

Jackson实现基于三个核心模块:jackson-corejackson-databindjackson-annotations

  • 流式解析器:Jackson在jackson-core中实现了高效的流式解析器JsonParser和生成器JsonGenerator。这些解析器可以在读取和写入时逐行处理数据,避免不必要的对象创建,减少内存使用。
  • 数据绑定jackson-databind负责对象-JSON之间的转换,通过反射和注解处理。
  • 扩展支持:通过模块化架构,Jackson可以扩展其他格式(如XML和YAML)。其实现通过Module接口定义和动态注入,实现了灵活的格式支持。

下面V哥通过源码分析来具体介绍:

Jackson 的源码实现涉及多个模块(如 jackson-corejackson-databindjackson-annotations 等),我们可以从 Jackson 的数据解析与生成的基本流程、数据绑定模块的实现、注解处理的方式、流式 API 等方面逐步分析其源码。

1. 流式 API(Streaming API)分析

Jackson 使用了流式解析器 JsonParser 和生成器 JsonGenerator,这使得它在处理大数据量 JSON 时具有较好的性能。jackson-core模块提供了这两个主要类。Jackson 的流式 API 是逐字节处理 JSON 数据的,因此可以实现低内存消耗和高效的数据处理。

代码解析示例

jackson-coreJsonParser 类中,实现了对 JSON 数据的逐步解析。它采用“令牌(Token)”的形式进行解析,常见的 Token 包括 START_OBJECTFIELD_NAMEVALUE_STRING 等。

  • JsonParser类的核心方法 nextToken()
    • 读取下一个 JSON Token,并将其存储在当前上下文中。
    • 通过 switch-case 结构,处理 JSON 字符串中的各类数据。
    • 例如,遇到"{"会生成 START_OBJECT,而 "}" 会对应 END_OBJECT
public JsonToken nextToken() throws IOException {// 实现不同字符的判断逻辑,根据 JSON 数据逐步解析int ch = nextByte();switch (ch) {case '{':_currToken = JsonToken.START_OBJECT;return _currToken;case '}':_currToken = JsonToken.END_OBJECT;return _currToken;// 省略其他分支}
}
  • JsonGenerator 类则用于写入 JSON,它将数据以 Token 的形式逐步生成,适用于输出大文件场景。
public void writeStartObject() throws IOException {_writeContext.writeStartObject();_generator.writeStartObject();
}
2. 数据绑定(Data Binding)分析

Jackson 的 jackson-databind 模块负责对象和 JSON 之间的自动映射和绑定。核心实现类是 ObjectMapper,它通过反射技术将 JSON 字段自动映射到 Java 对象字段。

代码解析示例

ObjectMapper 中主要通过 readValue()writeValue() 方法来实现 JSON 与对象的互转。

  • readValue()方法用于将 JSON 转换为对象。通过 DeserializationContextJsonDeserializer 的配合,它能够解析复杂的 JSON 数据到 Java 对象。
public <T> T readValue(JsonParser p, Class<T> valueType) throws IOException {JavaType javaType = _typeFactory.constructType(valueType);JsonDeserializer<Object> deser = _findRootDeserializer(_config, javaType);return (T) deser.deserialize(p, _deserializationContext);
}
  • writeValue() 方法用于将对象转换为 JSON 字符串。它会先通过 SerializationContextJsonSerializer 获取到需要的 JSON 结构,然后通过 JsonGenerator 写入。
public void writeValue(JsonGenerator gen, Object value) throws IOException {JsonSerializer<Object> serializer = _serializerProvider().findTypedValueSerializer(value.getClass(), true, null);serializer.serialize(value, gen, _serializerProvider());
}
3. 注解处理

Jackson 支持丰富的注解,例如 @JsonProperty@JsonIgnore 等,用于精细控制序列化和反序列化的过程。注解的处理主要依赖 AnnotationIntrospectorBeanSerializerFactory 等类。

代码解析示例

在 Jackson 中,@JsonProperty 注解通过 AnnotationIntrospectorfindPropertyNameForParam() 方法来获取指定的字段名,并对 Java 字段进行映射。

  • AnnotationIntrospector 类中会检查字段上是否存在 Jackson 注解,如果存在则执行注解对应的序列化规则。
public String findPropertyNameForParam(AnnotatedParameter param) {JsonProperty ann = param.getAnnotation(JsonProperty.class);return (ann == null) ? null : ann.value();
}
  • BeanSerializerFactory 类会解析所有字段和注解的关联关系,并生成一个 BeanSerializer 对象,用于在序列化时将注解信息加入。
public JsonSerializer<Object> createBeanSerializer(SerializerProvider prov, JavaType type, BeanDescription beanDesc) {BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc);List<BeanPropertyWriter> props = findBeanProperties(prov, beanDesc, builder);builder.setProperties(props);return builder.build();
}
4. 树模型(Tree Model)

Jackson 的树模型允许用户以树形结构操作 JSON 数据,例如 JsonNodeObjectNode 类,支持更灵活的数据访问与修改。树模型操作适合需要动态或未知 JSON 结构的场景。

  • JsonNode 是 Jackson 树模型的核心接口,ObjectNodeArrayNode 是其实现类,用于存储 JSON 对象和数组。
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonString);  // 读取 JSON 字符串
String name = rootNode.get("name").asText();       // 访问字段

JsonNode 的实现中,Jackson 提供了不同的子类来处理 JSON 数据节点,比如 TextNodeBooleanNodeArrayNode 等。这种设计使得 Jackson 能够轻松地在树节点中灵活读取和写入 JSON 数据。

5. 扩展与模块支持

Jackson 的模块化设计允许用户加载第三方模块来扩展其功能。jackson-module 系列包含了对 JDK8 类型、Java时间类、Kotlin 等支持。这些模块通过 Module 类来注册和管理,支持自定义序列化和反序列化器。

代码解析示例
  • 通过 registerModule() 方法可以动态加载扩展模块。
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule()); // 添加对 Java 时间类的支持
6. 小结一下

通过源码分析,V 哥发现,Jackson实在优秀,体现了优秀的设计架构,比如:流式解析提供了高性能支持,数据绑定实现了灵活的对象映射,注解处理让开发者可以细粒度控制序列化过程,同时还支持扩展模块。这种设计使得 Jackson 适用于多种复杂的 JSON 处理场景。

Gson 源码实现

再来看 Gson,Gson 的源码实现相对简洁,主要专注于 JSON 与 Java 对象的序列化和反序列化。其实现涉及的核心类包括 GsonTypeAdapterJsonElementJsonParser 等。下面我们分步骤分析 Gson 的主要源码实现过程。

1. Gson 的整体架构

Gson 的核心类是 Gson,它负责 JSON 与 Java 对象之间的相互转换。Gson 中的 toJson()fromJson() 方法是核心接口,它们分别用于将 Java 对象转换为 JSON 字符串,以及将 JSON 字符串解析为 Java 对象。Gson 的设计依赖于反射和注解,TypeAdapter 类用于自定义对象的序列化和反序列化。

2. JSON 转 Java 对象(反序列化)

Gson 中,fromJson() 方法用于 JSON 到 Java 对象的转换。其主要步骤包括解析 JSON、匹配类型、反射创建对象等。

代码解析示例
  • fromJson() 方法是 Gson 解析 JSON 字符串的入口。在调用时会根据给定的类型,将 JSON 转换为对应的 Java 对象。
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {// 使用 JsonReader 逐字符读取 JSON 数据JsonReader jsonReader = new JsonReader(new StringReader(json));return fromJson(jsonReader, classOfT);
}
  • fromJson(JsonReader reader, Type typeOfT) 方法通过调用 getAdapter(TypeToken.get(typeOfT)) 来获取 TypeAdapter,然后调用 read() 方法读取 JSON 数据并转换为对象。
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {TypeAdapter<T> adapter = getAdapter(TypeToken.get(typeOfT));return adapter.read(reader);
}
TypeAdapter

TypeAdapter 是 Gson 中的核心接口,用于控制对象序列化和反序列化的过程。Gson 提供了多种类型的 TypeAdapter,并支持用户自定义 TypeAdapter

  • read(JsonReader in) 方法会遍历 JSON 数据并创建 Java 对象。例如,在 ReflectiveTypeAdapterFactory.Adapter 中会通过反射读取 JSON 数据并赋值给 Java 对象的字段。
@Override
public T read(JsonReader in) throws IOException {T instance = constructor.construct();in.beginObject();while (in.hasNext()) {String name = in.nextName();// 找到字段对应的 TypeAdapter,赋值给 Java 对象Field field = fields.get(name);field.set(instance, fieldAdapter.read(in));}in.endObject();return instance;
}
3. Java 对象转 JSON(序列化)

Gson 的 toJson() 方法用于将 Java 对象序列化为 JSON 字符串。实现思路类似,首先获取对象的 TypeAdapter,然后通过 write() 方法将数据写入 JSON。

代码解析示例
  • toJson() 方法内部通过 getAdapter() 获取 TypeAdapter,然后调用 write() 生成 JSON。
public void toJson(Object src, Appendable writer) throws JsonIOException {if (src != null) {toJson(src, src.getClass(), writer);} else {writer.append("null");}
}
  • write(JsonWriter out, T value) 方法用于将 Java 对象写入 JSON。例如,在 ReflectiveTypeAdapterFactory.Adapter 中,它会逐字段将 Java 对象序列化为 JSON 字符串。
@Override
public void write(JsonWriter out, T value) throws IOException {out.beginObject();for (BoundField boundField : boundFields.values()) {out.name(boundField.name);boundField.write(out, value);}out.endObject();
}
JsonWriter

JsonWriter 是 Gson 中流式输出 JSON 的工具,配合 TypeAdapter 使用,它可以逐步生成 JSON,适合大对象和复杂结构。

4. TypeToken 的使用

Gson 使用 TypeToken 处理泛型,以解决 Java 的类型擦除问题。TypeToken 是 Gson 用于保存泛型类型信息的工具类。

  • 例如 new TypeToken<List<String>>(){}.getType() 可以获取 List<String>Type 对象。
Type listType = new TypeToken<List<String>>(){}.getType();
List<String> list = gson.fromJson(json, listType);
  • TypeToken 的源码实现利用了匿名内部类来获取泛型信息,TypeToken 的构造方法会调用 getSuperclassTypeParameter() 来捕获泛型类型。
private static Type getSuperclassTypeParameter(Class<?> subclass) {Type superclass = subclass.getGenericSuperclass();if (superclass instanceof ParameterizedType) {return ((ParameterizedType) superclass).getActualTypeArguments()[0];}throw new RuntimeException("Missing type parameter.");
}
5. 注解处理

Gson 支持注解 @SerializedName 来指定 JSON 字段名,它通过反射获取字段上的注解并设置到 Field 对象中,以在序列化和反序列化时使用。

  • ReflectiveTypeAdapterFactory 中,通过反射获取字段的注解。如果字段带有 @SerializedName 注解,则将注解指定的名称作为 JSON 中的字段名。
Field field = ...;
SerializedName annotation = field.getAnnotation(SerializedName.class);
String name = (annotation == null) ? field.getName() : annotation.value();
6. JsonElement 和 JsonParser

Gson 提供了 JsonElement 类来表示 JSON 节点,它是一个抽象类,具有 JsonObjectJsonArrayJsonPrimitiveJsonNull 等子类。

  • JsonParser 类用于解析 JSON 字符串并返回一个 JsonElement,适用于不确定 JSON 结构的动态解析场景。
JsonElement jsonTree = JsonParser.parseString(jsonString);
if (jsonTree.isJsonObject()) {JsonObject jsonObject = jsonTree.getAsJsonObject();// 可以根据键值访问String name = jsonObject.get("name").getAsString();
}
7. 自定义序列化与反序列化

Gson 允许用户通过 TypeAdapterJsonSerializer/JsonDeserializer 来自定义序列化和反序列化过程,适用于处理特殊格式的数据。

  • JsonSerializerJsonDeserializer 是接口,用于自定义序列化和反序列化过程,用户可以实现这两个接口,并将其传入 GsonBuilder 中进行注册。
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Date.class, new JsonSerializer<Date>() {@Overridepublic JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {return new JsonPrimitive(src.getTime()); // 序列化为时间戳}
});
Gson gson = builder.create();
8. GsonBuilder

GsonBuilder 是 Gson 的构建类,用于配置 Gson 实例。可以在 GsonBuilder 中添加自定义的 TypeAdapterJsonSerializerJsonDeserializer,并设置序列化/反序列化的策略。

GsonBuilder builder = new GsonBuilder();
builder.setPrettyPrinting(); // 格式化 JSON 输出
builder.registerTypeAdapter(CustomClass.class, new CustomTypeAdapter());
Gson gson = builder.create(); // 创建 Gson 实例
9. 小结一下

通过以上的源码分析,咱们可以看到,Gson 的源码设计相对简洁,适合处理简单的 JSON 数据结构。它的核心设计思想围绕 TypeAdapterTypeToken 和注解反射来实现灵活的序列化和反序列化。

4. 优缺点分析

Jackson和Gson各有优缺点,这也符合天下技术唯有最适合没有最好的道理, V 哥建议,在选择使用时,需要根据自己的项目情况来判断,才是明智的。

Jackson 优缺点
  • 优点

    • 性能出色,尤其是流式API适合大数据量解析。
    • 注解功能丰富,支持更细粒度的数据控制。
    • 支持多种数据格式,具有较强的扩展性。
    • 模块化架构,便于扩展和定制。
  • 缺点

    • API较复杂,学习成本较高。
    • 库的大小比Gson略大。
Gson 优缺点
  • 优点

    • 轻量级、易用,便于快速集成。
    • 注解支持基础操作,在简单映射场景中灵活性较高。
    • 更易于调试,且使用内存树模型方便解析和修改JSON。
  • 缺点

    • 性能相对较差,不适合大数据量和高并发处理场景。
    • 对复杂场景的支持较弱,尤其是序列化和反序列化定制能力欠缺。
    • 不支持流式API,内存开销较大。

5. 适用场景

从性能的角度来分析,咱们可以得出以下结论:

  • Jackson:适用于高并发、大数据量、高性能要求的场景,或需要复杂数据格式支持的应用。
  • Gson:适用于小规模的JSON处理、项目简单数据传输、快速开发等轻量级场景。

Jackson和Gson各有所长,选择时应根据具体需求权衡性能、灵活性和开发便捷性。

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

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

相关文章

【Visual Studio】设置文件目录

打开属性 输出目录&#xff1a;$(SolutionDir)bin\$(Platform)\$(Cinfiguration)\ 中间目录&#xff1a;$(SolutionDir)bin\intermediates\$(Platform)\$(Cinfiguration)\

基于Java的校园菜鸟驿站管理系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

Photoshop(PS)——人像磨皮

1.新建一个文件&#xff0c;背景为白色&#xff0c;将图片素材放入文件中 2.利用CtrlJ 复制两个图层出来&#xff0c;选择第一个拷贝图层&#xff0c;选择滤镜---杂色---蒙尘与划痕 3.调整一下数值&#xff0c;大概能够模糊痘印痘坑&#xff0c;点击确定。 4.然后选择拷贝2图层…

Yocto - 使用Yocto开发嵌入式Linux系统_13 创建定制层

Creating Custom Layers 除了使用社区或供应商提供的现有图层外&#xff0c;我们还将在本章中学习如何为我们的产品创建图层。此外&#xff0c;我们还将了解如何创建机器定义和分布&#xff0c;并从中获益&#xff0c;从而更好地组织我们的源代码。 In addition to using exist…

第5章-总体设计 5.2 需求转化为规格

5.2 需求转化为规格 1.框式产品&#xff08;1&#xff09;业务规格&#xff0c;这需要满足客户期望、有市场竞争力、颗粒度最合理。&#xff08;2&#xff09;整框规格&#xff0c;包括电源、功耗、散热、可靠性的规格&#xff0c;要保证整款满足环境应用要求。&#xff08;3&a…

kali上安装docker,并且生成centos7容器和创建apache容器后台运行

声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…

4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明

4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明 文章目录 4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明前言1. Ribbon 介绍1.1 LB(Load Balance 负载均衡) 2. Ribbon 原理2.2 Ribbon 机制 3. Spring Cloud Ribbon 实现负载均衡算法-应用实例4. 总结&#x…

约克VRF中央空调新天氟地水/天氟热水,做冬日生活的温暖守护者

随着冬季的悄然降临,现代人对居家环境的舒适性要求愈发提升,如何在寒冷的季节里营造一个温暖、静谧且健康的居住空间,成为了时下关注的焦点。面对冬日空气干燥、寒气侵袭的挑战,约克VRF中央空调凭借其氟系统和水系统的跨界融合,为家庭带来了纵享四季的恣意体验,让温暖与舒适触手…

计算机提示mfc140u.dll丢失的五种解决方法,了解mfc140u.dll错误的几种修复方法

当你尝试打开某些程序时&#xff0c;突然出现错误提示&#xff0c;告知你系统缺少 mfc140u.dll 文件&#xff0c;这可能让你感到困惑和无助。mfc140u.dll 是 Microsoft Foundation Class (MFC) 库的一部分&#xff0c;对于运行很多由 Visual Studio 2015 使用 MFC 开发的应用程…

【SSL-RL】自监督强化学习: 好奇心驱动探索 (CDE)算法

&#x1f4e2;本篇文章是博主强化学习&#xff08;RL&#xff09;领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对相关等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅…

Windows系统 ElasticSearch,Kibana安装

目录 1.wins安装ElasticSearch2.将 elasticsearch 以服务的方式安装3. 在系统环境变量 Path 中添加如下路径4.启动点击即可5.双击 elasticsearch.bat 启动 elasticsearch 服务6.启动后第一次会显示一些配置信息,包括默认的用户密码 先记住 记不住的话可以重置密码7.验证安装结果…

《Probing the 3D Awareness of Visual Foundation Models》论文解析——单图像表面重建

一、论文简介 论文讨论了大规模预训练产生的视觉基础模型在处理任意图像时的强大能力&#xff0c;这些模型不仅能够完成训练任务&#xff0c;其中间表示还对其他视觉任务&#xff08;如检测和分割&#xff09;有用。研究者们提出了一个问题&#xff1a;这些模型是否能够表示物体…

P3-4.【结构化程序设计】第四节——知识要点:break、continue和goto辅助循环设计语句

知识要点&#xff1a;break、continue和goto辅助循环设计语句 视频&#xff1a; P3-4.1.【结构化程序设计】第四节——知识要点&#xff1a;break、continue和goto辅助循环设计语句 P3-4.2.【结构化程序设计】第四节——知识要点&#xff1a;break、continue和goto辅助循环设计…

灵神DAY3 KMP算法

具体解释&#xff1a; 1. 真前缀和真后缀的定义 前缀&#xff1a;字符串的起始部分。例如&#xff0c;字符串 s "aabcaa" 的前缀是 ""、"a"、"aa"、"aab"、"aabc"、"aabca"、"aabcaa"。 …

MySQL5.7.37安装配置

1.下载MySQL软件包并解压 2.配置环境变量 3.新建my.ini文件并输入信息 [mysqld] #端口号 port 3306 #mysql-5.7.27-winx64的路径 basedirC:\mysql-5.7.37\mysql-5.7.37-winx64 #mysql-5.7.27-winx64的路径\data datadirC:\mysql-5.7.37\mysql-5.7.37-winx64\data #最大连接数…

基于单片机的手持金属探测仪设计

本设计以STM32F103C8T6单片机为核心&#xff0c;通过金属线圈感应器来判断是否存在金属&#xff0c;控制OLED显示屏显示金属探测仪的灵敏度和参考值&#xff0c;通过电源模块将220V转化为3.3V对单片机进行供电&#xff0c;还可以通过按键对金属探测仪的灵敏度进行设置&#xff…

P1197 星球大战(并查集+逆向思维)

这是今天写的比较有价值的一道题&#xff0c;晚上写了大概一个多小时&#xff0c;主要还是在debug&#xff0c;出得很妙&#xff0c;好题&#x1f44d; P1197 [JSOI2008] 星球大战 - 洛谷 | 计算机科学教育新生态 思路&#xff1a;如果我们按照顺序一个一个的去计算毁灭一个星…

深度学习驱动的蛋白质设计技术与前沿实践-从基础到尖端应用

RoseTTAFold&#xff0c;作为David Baker教授团队早期开发的蛋白质结构预测工具&#xff0c;在学术界与工业界广受认可。然而&#xff0c;随着时间推移&#xff0c;仅局限于预测已知结构的蛋白质并不能满足生物医药和生物工程领域对创新设计的需求。这促使David Baker教授团队继…

Linux 进程信号初识

目录 0.前言 1.什么是信号 1.1生活中的信号 1.2 OS中的信号 2.认识信号 2.1信号概念 2.2查看信号 2.3 signal函数 2.4代码示例 3. 信号处理方式 3.1 忽略信号 3.2 默认处理 3.3 自定义处理 4.小结 &#xff08;图像由AI生成&#xff09; 0.前言 在之前的学习中&#xff0c;我…

SpringBoot(二十五)SpringBoot集成JRebel实现热更新

今天来安装一个IDEA代码热更新的插件,一个神器。 我们之前也为IDEA配置了热更新,使用的是spring-boot-devtools插件。具体请移步《SpringBoot(一)创建项目及配置IDEA热更新》 上边这个热更新对于单模块项目是没有问题的,但是对于多模块项目可能就无能无能为力了,而且,随…