云计算百科
云计算领域专业知识百科平台

第2课:SpringBoot4.x 项目构建:Maven/Gradle配置详解,父工程与依赖管理

文章目录

    • 1. 课前导读
    • 2. 知识目标
    • 3. 理论讲解
      • 3.1 为什么Spring Boot 4.x要重构依赖体系
      • 3.2 Maven依赖管理三层模型
      • 3.3 Spring Boot 4.x模块化对依赖坐标的影响
      • 3.4 Gradle配置的核心变化
    • 4. 环境搭建
      • 4.1 Maven方式一:继承spring-boot-starter-parent(推荐单模块项目)
      • 4.2 Maven方式二:导入BOM(推荐多模块项目)
      • 4.3 Maven多模块项目最佳实践
      • 4.4 Gradle配置详解
      • 4.5 使用版本目录(Version Catalog)
    • 5. 代码实战
      • 5.1 完整的多模块项目示例
      • 5.2 运行与验证
      • 5.3 依赖冲突排查实战
    • 6. 底层源码剖析
      • 6.1 spring-boot-starter-parent的继承机制
      • 6.2 spring-boot-dependencies BOM的结构
      • 6.3 spring-boot-maven-plugin的repackage目标
    • 7. 常见坑点与生产问题排查
      • 坑点1:spring-boot-starter-web 在4.x中已重命名
      • 坑点2:多模块项目中父POM误用plugins导致repackage全家桶
      • 坑点3:BOM导入后仍需要手动指定版本
      • 坑点4:Jackson 3包名迁移导致自定义配置编译失败
      • 坑点5:Gradle版本过低导致插件不兼容
      • 坑点6:第三方Starter未适配4.x模块化
    • 8. 本章总结
    • 9. 课后作业
      • 基础题1
      • 基础题2
      • 综合实战题
  • 🔗 SpringBoot 4.x 从入门到企业级实战系列课程

在这里插入图片描述

本文是《SpringBoot 4.x 从入门到精通》系列第2课,全文约13000字,建议阅读时间50分钟。 环境要求:Spring Boot 4.x,Spring Framework 7,Java 17+(推荐Java 21/25)


1. 课前导读

上一课我们完成了Spring Boot 4.x的HelloWorld程序,相信你已经感受到了4.x的“开箱即用”。但你有没有想过一个问题:为什么pom.xml里不写版本号也能正常工作?为什么spring-boot-starter-webmvc能自动引入Tomcat、Spring MVC、Jackson等一系列依赖?

答案就在项目构建和依赖管理这两个核心机制里。

Spring Boot 4.0对项目构建体系做了自3.0以来最大的一次重构。spring-boot-starter-web被重命名为spring-boot-starter-webmvc,单一的spring-boot-autoconfigure模块被拆分为多个聚焦的小模块,每个模块都有独立的starter和test-starter。这意味着依赖坐标变了、依赖树变了、父工程的用法也变了。

如果你的企业项目中有多模块架构,或者你有自定义的父工程需要统一管理版本,这节课的内容将直接决定你的项目能否顺利迁移到4.x。

前置知识:Maven或Gradle基本使用、了解dependencyManagement和dependencies的区别。

学习难点:理解Spring Boot 4.x模块化对依赖坐标的影响;掌握父工程+BOM+Starter的正确分工;区分spring-boot-starter-parent与spring-boot-dependencies的使用场景。

【配图描述:Spring Boot 4.x 依赖管理三层模型图——Parent(规则继承)、BOM(版本管理)、Starter(功能引入)】


2. 知识目标

学完本节课,你必须掌握以下核心知识点:

  • Spring Boot 4.x Maven配置完整体系:spring-boot-starter-parent继承方式、spring-boot-dependencies BOM导入方式、spring-boot-maven-plugin插件的配置与执行。
  • Spring Boot 4.x Gradle配置:plugins DSL声明、io.spring.dependency-management插件、platform()BOM导入、版本目录(Version Catalog)的使用。
  • 模块化Starter的依赖坐标变化:spring-boot-starter-web→spring-boot-starter-webmvc,各模块test-starter的独立化,classic starter的过渡方案。
  • 多模块项目中的父工程最佳实践:父POM使用BOM管理版本、子模块通过starter引入功能、插件放在pluginManagement中按需启用。
  • 依赖冲突排查与版本覆盖:mvn dependency:tree的使用、BOM属性的覆盖方式、第三方库版本对齐策略。

  • 3. 理论讲解

    3.1 为什么Spring Boot 4.x要重构依赖体系

    在理解4.x的依赖管理之前,我们需要先搞清楚一个根本问题:Spring Boot 3.x的依赖体系有什么问题?

    Spring Boot 3.x中,所有的自动配置类都集中在一个约2MB的spring-boot-autoconfigure JAR包中。无论你的应用是Web应用、批处理应用还是消息驱动应用,这个JAR包都会被完整加载到classpath中。类加载器需要扫描这个JAR包中所有的AutoConfiguration.imports文件,然后根据条件注解逐一判断哪些配置类需要生效。

    这带来了三个问题:

    启动开销。 即使你的应用只用了Web和JDBC,JPA、Security、Redis、Kafka等模块的自动配置类仍然会被加载到JVM中,只是不生效而已。类加载和条件判断都消耗了启动时间。

    内存浪费。 未使用的自动配置类及其依赖的类仍然占用元空间(Metaspace)内存。

    模块耦合。 第三方Starter如果想提供自动配置,必须依赖整个spring-boot-autoconfigure模块,即使它只需要其中的一两个注解。

    Spring Boot 4.0的模块化重构正是为了解决这些问题。将spring-boot-autoconfigure拆分为spring-boot-webmvc、spring-boot-jdbc、spring-boot-data-jpa等多个聚焦模块后,应用只加载实际引入的模块中的自动配置类。

    【配图描述:Spring Boot 3.x单体autoconfigure vs 4.x模块化autoconfigure的类加载范围对比图】

    3.2 Maven依赖管理三层模型

    Spring Boot的Maven依赖管理可以抽象为三个层次,理解这个模型是掌握4.x项目构建的关键。

    第一层:Parent(规则继承)。 spring-boot-starter-parent是Spring Boot提供的“约定优于配置”的体现。它定义了Java编译版本、UTF-8编码、资源过滤规则、插件配置等构建规范。继承Parent意味着你的项目自动获得了这些默认配置,不需要在每个项目中重复编写。

    第二层:BOM(版本管理)。 spring-boot-dependencies是一个BOM(Bill of Materials),只做一件事:管理依赖的版本。它不引入任何JAR包,只是声明了“如果使用这个依赖,应该用哪个版本”。Parent内部自动导入了这个BOM,所以继承Parent的项目可以省略<version>标签。

    第三层:Starter(功能引入)。 spring-boot-starter-webmvc等Starter才是真正“引入JAR包并启用功能”的地方。一个Starter通常会引入多个依赖(如Spring MVC、Tomcat、Jackson)并触发相应的自动配置。

    这三层的关系可以用一句话概括:Parent管规则,BOM管版本,Starter管功能。在Spring Boot 4.x的多模块项目中,推荐的做法是——父POM通过dependencyManagement导入BOM来管理版本,子模块依赖Starter来启用功能,插件放在pluginManagement中按需启用。

    3.3 Spring Boot 4.x模块化对依赖坐标的影响

    这是4.x项目构建中最容易踩坑的地方。模块化重构直接改变了Starter的坐标名称。

    Web模块的变化。 在3.x中,Web项目依赖spring-boot-starter-web。在4.x中,这个Starter被重命名为spring-boot-starter-webmvc。对应的测试依赖也从spring-boot-starter-test变为spring-boot-starter-webmvc-test。

    Test Starter的独立化。 3.x中有一个统一的spring-boot-starter-test,包含JUnit、Mockito、AssertJ等所有测试工具。4.x中,每个功能模块都有独立的test-starter,例如spring-boot-starter-webmvc-test、spring-boot-starter-data-jpa-test、spring-boot-starter-security-test。这意味着测试依赖更加精准——测试Web层不需要引入JPA的测试工具。

    Classic Starter过渡方案。 为了兼容大量现有项目,Spring Boot 4.0提供了“Classic Starter POMs”。这些Classic Starter包含对应模块的所有依赖,但排除了所有传递依赖。如果你在升级现有项目时不想逐个调整依赖坐标,可以暂时使用Classic Starter,但这只是一种过渡方案。

    【配图描述:Spring Boot 3.x vs 4.x Starter坐标对照表】

    3.4 Gradle配置的核心变化

    Gradle用户同样需要关注4.x的变化。Spring Boot 4.x的Gradle插件版本同步升级到4.x,要求Gradle 7.6.4+或8.4+。

    在Gradle中,Spring Boot的依赖管理有两种方式:

    方式一:使用io.spring.dependency-management插件。 这是传统方式,插件会自动导入Spring Boot的BOM,使得依赖声明可以省略版本号。

    方式二:使用platform()导入BOM。 这是Gradle原生方式,通过implementation platform("org.springframework.boot:spring-boot-dependencies:4.0.6")来导入BOM。Spring Boot官方推荐在新项目中采用这种方式,因为它不依赖额外插件,更加透明。

    此外,Gradle用户应该关注版本目录(Version Catalog) 的用法。4.x的模块化使得每个功能模块有独立的starter坐标,使用版本目录可以在libs.versions.toml中统一定义所有Starter别名,避免在build.gradle中硬编码坐标。


    4. 环境搭建

    4.1 Maven方式一:继承spring-boot-starter-parent(推荐单模块项目)

    这是最简单的方式,适用于单模块项目。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    https://maven.apache.org/xsd/maven-4.0.0.xsd"
    >

    <modelVersion>4.0.0</modelVersion>

    <!– 继承 Spring Boot 4.x Parent,获得构建默认配置 –>
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>4.0.8</version>
    <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>single-module-app</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>single-module-app</name>

    <properties>
    <java.version>21</java.version>
    </properties>

    <dependencies>
    <!– Spring Boot 4.x Web MVC Starter(替代3.x的starter-web) –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webmvc</artifactId>
    </dependency>

    <!– 测试依赖(4.x独立化的test-starter) –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webmvc-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>

    <build>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    </build>
    </project>

    关键点:继承Parent后,spring-boot-starter-webmvc和spring-boot-starter-webmvc-test都不需要写<version>标签。Parent自动导入了spring-boot-dependencies BOM,所有Spring Boot管理的依赖版本都由BOM决定。

    4.2 Maven方式二:导入BOM(推荐多模块项目)

    如果你的企业已有自己的父工程,不能继承spring-boot-starter-parent,可以使用BOM导入方式。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    https://maven.apache.org/xsd/maven-4.0.0.xsd"
    >

    <modelVersion>4.0.0</modelVersion>

    <!– 企业自有父工程 –>
    <parent>
    <groupId>com.example</groupId>
    <artifactId>company-parent</artifactId>
    <version>2.0.0</version>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>standalone-app</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <properties>
    <java.version>21</java.version>
    <spring-boot.version>4.0.8</spring-boot.version>
    </properties>

    <dependencyManagement>
    <dependencies>
    <!– 导入 Spring Boot BOM,只管理版本,不引入依赖 –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>${spring-boot.version}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    </dependencies>
    </dependencyManagement>

    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webmvc</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webmvc-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>

    <build>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    </build>
    </project>

    注意:使用这种方式时,需要手动配置spring-boot-maven-plugin的版本(通常放在父工程的pluginManagement中统一管理)。同时,Java编译版本、UTF-8编码等构建配置也需要手动声明,因为不再继承Spring Boot Parent。

    4.3 Maven多模块项目最佳实践

    这是企业项目中最重要的配置模式。假设你有一个多模块项目,结构如下:

    company-project/
    ├── pom.xml (父POM:只做版本管理和插件管理)
    ├── common/ (通用模块:不含Spring Boot功能)
    │ └── pom.xml
    ├── api/ (API模块:DTO和接口定义)
    │ └── pom.xml
    └── web/ (Web模块:Spring Boot应用)
    └── pom.xml

    父POM(company-project/pom.xml)

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    https://maven.apache.org/xsd/maven-4.0.0.xsd"
    >

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>company-project</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
    <module>common</module>
    <module>api</module>
    <module>web</module>
    </modules>

    <properties>
    <java.version>21</java.version>
    <spring-boot.version>4.0.8</spring-boot.version>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencyManagement>
    <dependencies>
    <!– 导入 Spring Boot BOM –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>${spring-boot.version}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    <!– 内部模块版本管理 –>
    <dependency>
    <groupId>com.example</groupId>
    <artifactId>common</artifactId>
    <version>${project.version}</version>
    </dependency>
    <dependency>
    <groupId>com.example</groupId>
    <artifactId>api</artifactId>
    <version>${project.version}</version>
    </dependency>
    </dependencies>
    </dependencyManagement>

    <build>
    <pluginManagement>
    <plugins>
    <!– 插件放在 pluginManagement 中,子模块按需启用 –>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>${spring-boot.version}</version>
    </plugin>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.13.0</version>
    <configuration>
    <release>${java.version}</release>
    </configuration>
    </plugin>
    </plugins>
    </pluginManagement>
    </build>
    </project>

    Web子模块(web/pom.xml)

    <project …>
    <parent>
    <groupId>com.example</groupId>
    <artifactId>company-project</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>web</artifactId>

    <dependencies>
    <!– 内部模块依赖 –>
    <dependency>
    <groupId>com.example</groupId>
    <artifactId>common</artifactId>
    </dependency>
    <dependency>
    <groupId>com.example</groupId>
    <artifactId>api</artifactId>
    </dependency>

    <!– Spring Boot Starter(版本由父POM的BOM管理) –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webmvc</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webmvc-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>

    <build>
    <plugins>
    <!– 只有web模块需要打包成可执行JAR –>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    </build>
    </project>

    核心原则:父POM用BOM定版本,子模块用Starter启功能,插件放在pluginManagement中按需启用。父POM不应该放在<plugins>中,否则所有子模块(包括common和api)都会执行repackage,导致“全家一起打包成Boot应用”的事故。

    4.4 Gradle配置详解

    build.gradle(Groovy DSL)

    plugins {
    id 'java'
    id 'org.springframework.boot' version '4.0.8'
    id 'io.spring.dependency-management' version '1.1.7'
    }

    group = 'com.example'
    version = '1.0.0-SNAPSHOT'

    java {
    toolchain {
    languageVersion = JavaLanguageVersion.of(21)
    }
    }

    repositories {
    mavenCentral()
    }

    dependencies {
    // Spring Boot 4.x Web MVC Starter
    implementation 'org.springframework.boot:spring-boot-starter-webmvc'

    // 测试 Starter
    testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
    }

    tasks.named('test') {
    useJUnitPlatform()
    }

    Gradle原生BOM导入方式(推荐)

    如果你不想使用io.spring.dependency-management插件,可以使用Gradle原生的platform():

    plugins {
    id 'java'
    id 'org.springframework.boot' version '4.0.8'
    }

    dependencies {
    // 导入 Spring Boot BOM
    implementation platform('org.springframework.boot:spring-boot-dependencies:4.0.8')

    implementation 'org.springframework.boot:spring-boot-starter-webmvc'
    testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
    }

    Gradle多模块项目配置

    根项目settings.gradle:

    rootProject.name = 'company-project'
    include 'common'
    include 'api'
    include 'web'

    根项目build.gradle:

    plugins {
    id 'java' apply false
    id 'org.springframework.boot' version '4.0.8' apply false
    }

    subprojects {
    apply plugin: 'java'

    java {
    toolchain {
    languageVersion = JavaLanguageVersion.of(21)
    }
    }

    repositories {
    mavenCentral()
    }

    dependencies {
    implementation platform('org.springframework.boot:spring-boot-dependencies:4.0.8')
    }
    }

    web/build.gradle:

    plugins {
    id 'org.springframework.boot'
    }

    dependencies {
    implementation project(':common')
    implementation project(':api')
    implementation 'org.springframework.boot:spring-boot-starter-webmvc'
    testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
    }

    4.5 使用版本目录(Version Catalog)

    Gradle 7.0+引入了版本目录特性,在Spring Boot 4.x的多模块项目中尤为有用。

    gradle/libs.versions.toml:

    [versions]
    spring-boot = "4.0.8"
    java = "21"

    [libraries]
    spring-boot-webmvc = { module = "org.springframework.boot:spring-boot-starter-webmvc" }
    spring-boot-webmvc-test = { module = "org.springframework.boot:spring-boot-starter-webmvc-test" }

    [plugins]
    spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot" }

    build.gradle中使用:

    plugins {
    alias(libs.plugins.spring.boot)
    }

    dependencies {
    implementation platform("org.springframework.boot:spring-boot-dependencies:${libs.versions.spring.boot.get()}")
    implementation libs.spring.boot.webmvc
    testImplementation libs.spring.boot.webmvc.test
    }


    5. 代码实战

    5.1 完整的多模块项目示例

    让我们创建一个完整的可运行多模块项目,包含通用工具模块、API模块和Web模块。

    项目结构

    multi-module-demo/
    ├── pom.xml
    ├── common/
    │ ├── pom.xml
    │ └── src/main/java/com/example/common/
    │ └── util/StringUtils.java
    ├── api/
    │ ├── pom.xml
    │ └── src/main/java/com/example/api/
    │ └── dto/UserDTO.java
    └── web/
    ├── pom.xml
    └── src/main/
    ├── java/com/example/web/
    │ ├── MultiModuleApplication.java
    │ └── controller/UserController.java
    └── resources/application.yml

    common模块的StringUtils.java

    package com.example.common.util;

    /**
    * 通用字符串工具类。
    * 该模块不依赖 Spring Boot,只包含纯 Java 逻辑。
    */

    public final class StringUtils {

    private StringUtils() {}

    /**
    * 判断字符串是否为空白(null、空串、仅空格)。
    */

    public static boolean isBlank(String str) {
    return str == null || str.trim().isEmpty();
    }

    /**
    * 反转字符串,null 安全。
    */

    public static String reverse(String str) {
    if (str == null) return null;
    return new StringBuilder(str).reverse().toString();
    }
    }

    api模块的UserDTO.java

    package com.example.api.dto;

    import java.time.LocalDateTime;

    /**
    * 用户数据传输对象。
    * 该模块只包含 DTO 定义,被 web 模块引用。
    */

    public class UserDTO {

    private Long id;
    private String name;
    private String email;
    private LocalDateTime createdAt;

    // 无参构造器
    public UserDTO() {}

    public UserDTO(Long id, String name, String email) {
    this.id = id;
    this.name = name;
    this.email = email;
    this.createdAt = LocalDateTime.now();
    }

    // getter / setter 省略,实际代码中需完整实现
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public LocalDateTime getCreatedAt() { return createdAt; }
    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
    }

    web模块的启动类

    package com.example.web;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;

    /**
    * 多模块 Web 应用启动入口。
    *
    * 注意:@SpringBootApplication 的扫描范围是 com.example.web 及子包。
    * common 和 api 模块的类需要通过 @Import 或 @ComponentScan 显式引入。
    */

    @SpringBootApplication
    public class MultiModuleApplication {
    public static void main(String[] args) {
    SpringApplication.run(MultiModuleApplication.class, args);
    }
    }

    web模块的Controller

    package com.example.web.controller;

    import com.example.api.dto.UserDTO;
    import com.example.common.util.StringUtils;
    import org.springframework.web.bind.annotation.*;

    import java.util.List;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.atomic.AtomicLong;

    /**
    * 用户管理接口,演示多模块依赖。
    */

    @RestController
    @RequestMapping("/api/users")
    public class UserController {

    private final ConcurrentHashMap<Long, UserDTO> store = new ConcurrentHashMap<>();
    private final AtomicLong idGenerator = new AtomicLong(1);

    /**
    * 创建用户。
    * POST /api/users
    * Body: {"name":"张三","email":"zhangsan@example.com"}
    */

    @PostMapping
    public UserDTO create(@RequestBody UserDTO user) {
    if (StringUtils.isBlank(user.getName())) {
    throw new IllegalArgumentException("用户名不能为空");
    }
    long id = idGenerator.getAndIncrement();
    UserDTO saved = new UserDTO(id, user.getName(), user.getEmail());
    store.put(id, saved);
    return saved;
    }

    /**
    * 获取所有用户。
    * GET /api/users
    */

    @GetMapping
    public List<UserDTO> list() {
    return List.copyOf(store.values());
    }

    /**
    * 根据 ID 获取用户。
    * GET /api/users/{id}
    */

    @GetMapping("/{id}")
    public UserDTO getById(@PathVariable Long id) {
    UserDTO user = store.get(id);
    if (user == null) {
    throw new IllegalArgumentException("用户不存在: " + id);
    }
    return user;
    }

    /**
    * 演示 StringUtils 的使用。
    * GET /api/users/reverse?input=hello
    */

    @GetMapping("/reverse")
    public String reverse(@RequestParam String input) {
    return StringUtils.reverse(input);
    }
    }

    5.2 运行与验证

    编译整个项目

    cd multi-module-demo
    mvn clean install

    启动Web模块

    cd web
    mvn spring-boot:run

    测试接口

    # 创建用户
    curl -X POST http://localhost:8080/api/users \\
    -H "Content-Type: application/json" \\
    -d '{"name":"张三","email":"zhangsan@example.com"}'
    # 输出: {"id":1,"name":"张三","email":"zhangsan@example.com","createdAt":"2025-12-01T10:30:00"}

    # 获取所有用户
    curl http://localhost:8080/api/users
    # 输出: [{"id":1,"name":"张三","email":"zhangsan@example.com","createdAt":"…"}]

    # 字符串反转
    curl "http://localhost:8080/api/users/reverse?input=hello"
    # 输出: olleh

    5.3 依赖冲突排查实战

    假设项目中出现了一个依赖冲突场景:common模块引入了commons-lang3:3.12.0,而Spring Boot BOM管理的是3.14.0。运行mvn dependency:tree可以看到:

    [INFO] com.example:web:jar:1.0.0-SNAPSHOT
    [INFO] +- com.example:common:jar:1.0.0-SNAPSHOT
    [INFO] | \\- org.apache.commons:commons-lang3:jar:3.12.0:compile
    [INFO] +- org.springframework.boot:spring-boot-starter-webmvc:jar:4.0.8:compile
    [INFO] | \\- (org.apache.commons:commons-lang3:jar:3.14.0:compile – omitted for duplicate)

    由于Maven的“最近优先”规则,commons-lang3:3.12.0被选中,与Spring Boot管理的版本不一致。解决方案是在父POM中覆盖BOM属性:

    <properties>
    <commons-lang3.version>3.14.0</commons-lang3.version>
    </properties>

    或者在dependencyManagement中显式声明:

    <dependencyManagement>
    <dependencies>
    <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.14.0</version>
    </dependency>
    </dependencies>
    </dependencyManagement>


    6. 底层源码剖析

    6.1 spring-boot-starter-parent的继承机制

    spring-boot-starter-parent的pom.xml本身非常简洁,它的核心作用是通过继承链将构建配置传递给子项目。

    <!– spring-boot-starter-parent-4.0.8.pom 核心内容 –>
    <project>
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>4.0.8</version>
    <relativePath>../spring-boot-dependencies</relativePath>
    </parent>

    <artifactId>spring-boot-starter-parent</artifactId>
    <packaging>pom</packaging>

    <properties>
    <java.version>17</java.version>
    <maven.compiler.release>${java.version}</maven.compiler.release>
    <resource.delimiter>@</resource.delimiter>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
    <pluginManagement>
    <plugins>
    <!– 配置 spring-boot-maven-plugin 的默认执行 –>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
    <execution>
    <id>repackage</id>
    <goals>
    <goal>repackage</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </pluginManagement>
    </build>
    </project>

    关键点:spring-boot-starter-parent继承了spring-boot-dependencies,因此子项目继承Parent后自动获得了所有依赖版本管理。同时,Parent在pluginManagement中定义了spring-boot-maven-plugin的默认执行(repackage),子项目只需要在<plugins>中声明插件即可触发可执行JAR打包。

    6.2 spring-boot-dependencies BOM的结构

    spring-boot-dependencies是一个纯粹的BOM,它的结构如下:

    <project>
    <artifactId>spring-boot-dependencies</artifactId>
    <packaging>pom</packaging>

    <properties>
    <spring-framework.version>7.0.9</spring-framework.version>
    <jackson-bom.version>3.0.0</jackson-bom.version>
    <tomcat.version>11.0.0</tomcat.version>
    <hikaricp.version>7.0.0</hikaricp.version>
    <!– … 数百个版本属性 –>
    </properties>

    <dependencyManagement>
    <dependencies>
    <!– Spring Framework BOM –>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-framework-bom</artifactId>
    <version>${spring-framework.version}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    <!– Jackson 3 BOM –>
    <dependency>
    <groupId>tools.jackson</groupId>
    <artifactId>jackson-bom</artifactId>
    <version>${jackson-bom.version}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    <!– Spring Boot Starter 的版本声明 –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webmvc</artifactId>
    <version>${project.version}</version>
    </dependency>
    <!– … –>
    </dependencies>
    </dependencyManagement>
    </project>

    <dependencyManagement>中的依赖声明不会引入JAR包,只是告诉Maven“如果项目使用了这个依赖,应该用这个版本”。当子项目的<dependencies>中声明了spring-boot-starter-webmvc且没有指定版本时,Maven会从dependencyManagement中查找版本。

    6.3 spring-boot-maven-plugin的repackage目标

    spring-boot-maven-plugin的核心功能是repackage目标,它将Maven生成的普通JAR重新打包为可执行的Spring Boot JAR。在4.x中,repackage的执行流程与3.x基本一致:

    // 源码路径:org.springframework.boot.maven.RepackageMojo
    public class RepackageMojo extends AbstractPackagerMojo {

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
    // 1. 获取原始 JAR 文件
    File source = this.project.getArtifact().getFile();
    // 2. 创建可执行 JAR
    File target = getTargetFile();
    // 3. 执行重新打包
    repackage(source, target);
    // 4. 替换原始 JAR
    // …
    }
    }

    4.x的关键变化在于分层打包(Layered JAR)的默认启用。分层打包将应用代码和依赖库分离到不同的层中,Docker构建时可以复用依赖层,显著加快镜像构建速度。分层信息写入JAR中的layers.idx文件。


    7. 常见坑点与生产问题排查

    坑点1:spring-boot-starter-web 在4.x中已重命名

    报错现象:使用spring-boot-starter-web时IDE提示依赖已废弃,或无法找到某些自动配置类。

    原因:Spring Boot 4.0将spring-boot-starter-web重命名为spring-boot-starter-webmvc,对应的测试Starter也从spring-boot-starter-test变为spring-boot-starter-webmvc-test。

    解决方案:将依赖坐标改为spring-boot-starter-webmvc和spring-boot-starter-webmvc-test。

    坑点2:多模块项目中父POM误用plugins导致repackage全家桶

    报错现象:common和api模块也被打包成可执行JAR,启动时报“no main manifest attribute”。

    原因:spring-boot-maven-plugin被放在父POM的<plugins>中(而非<pluginManagement>),导致所有子模块都继承了repackage的执行。

    解决方案:将插件移到父POM的<pluginManagement>中,只在需要打包成Boot应用的子模块(如web)的<plugins>中声明。

    坑点3:BOM导入后仍需要手动指定版本

    报错现象:导入spring-boot-dependencies BOM后,依赖仍然报“version is missing”。

    原因:<scope>import</scope>必须配合<type>pom</type>使用,且必须放在<dependencyManagement>中。如果放在<dependencies>中,BOM本身会被当作依赖引入,但版本管理不生效。

    解决方案:确保BOM依赖声明在<dependencyManagement>中,包含<type>pom</type>和<scope>import</scope>。

    坑点4:Jackson 3包名迁移导致自定义配置编译失败

    报错现象:import com.fasterxml.jackson.databind.ObjectMapper;编译失败。

    原因:Spring Boot 4.0默认使用Jackson 3,包名从com.fasterxml.jackson迁移到tools.jackson。

    解决方案:将自定义Jackson配置的import改为tools.jackson.databind.ObjectMapper。

    坑点5:Gradle版本过低导致插件不兼容

    报错现象:Plugin [id: 'org.springframework.boot', version: '4.0.8'] was not found。

    原因:Spring Boot 4.x的Gradle插件要求Gradle 7.6.4+或8.4+。

    解决方案:升级Gradle版本,或在gradle/wrapper/gradle-wrapper.properties中修改distributionUrl。

    坑点6:第三方Starter未适配4.x模块化

    报错现象:引入第三方Starter(如某些国产ORM框架的Spring Boot Starter)后,自动配置不生效。

    原因:第三方Starter可能依赖了spring-boot-autoconfigure的内部类,而这些类在4.x中变为package-private或移动了位置。

    解决方案:检查第三方Starter是否已发布4.x兼容版本;如果还没有,暂时使用Classic Starter过渡,或联系维护者更新。


    8. 本章总结

    本节课围绕Spring Boot 4.x的项目构建和依赖管理,从Maven和Gradle两个维度构建了完整的知识体系。

    Maven三层模型:Parent管规则(构建配置、插件默认值),BOM管版本(spring-boot-dependencies),Starter管功能(引入依赖和自动配置)。单模块项目推荐继承spring-boot-starter-parent,多模块项目推荐父POM导入BOM + 子模块依赖Starter。

    4.x模块化变化:spring-boot-starter-web→spring-boot-starter-webmvc,Test Starter独立化为各模块的-test后缀,Classic Starter作为过渡方案保留。自动配置类从单一spring-boot-autoconfigure模块分散到各功能模块中。

    Gradle配置:Spring Boot 4.x插件要求Gradle 7.6.4+,推荐使用platform()导入BOM,版本目录(Version Catalog)在多模块项目中管理Starter坐标。

    多模块最佳实践:父POM用BOM定版本,子模块用Starter启功能,插件放在pluginManagement中按需启用。父POM不应放在<plugins>中,避免“全家桶repackage”事故。


    9. 课后作业

    基础题1

    创建一个单模块Spring Boot 4.x项目,分别使用以下两种方式管理依赖版本:

    • 方式A:继承spring-boot-starter-parent
    • 方式B:导入spring-boot-dependencies BOM

    对比两种方式的pom.xml差异,运行mvn dependency:tree,记录输出结果,解释为什么方式A可以省略spring-boot-maven-plugin的版本号。

    基础题2

    使用Gradle创建一个Spring Boot 4.x项目,分别使用io.spring.dependency-management插件和platform()导入BOM。观察gradle dependencies输出的差异,记录BOM是否被正确导入。

    综合实战题

    创建一个完整的多模块Maven项目,包含以下三个模块:

  • common模块:包含DateUtils工具类,提供format(LocalDateTime)方法返回yyyy-MM-dd HH:mm:ss格式字符串,不依赖Spring Boot。
  • api模块:包含OrderDTO类,字段包括orderId(String)、amount(BigDecimal)、createdAt(LocalDateTime),依赖common模块。
  • web模块:Spring Boot 4.x应用,依赖common和api模块,提供REST接口:
    • POST /api/orders:创建订单,返回OrderDTO
    • GET /api/orders:返回所有订单列表
    • GET /api/orders/{id}/time:返回订单创建时间的格式化字符串(使用DateUtils)
  • 要求:

    • 父POM使用BOM导入方式管理Spring Boot版本
    • 父POM的插件配置放在pluginManagement中
    • 只有web模块声明spring-boot-maven-plugin在<plugins>中
    • 使用mvn clean install编译整个项目
    • 使用mvn dependency:tree检查依赖树,确保没有版本冲突
    • 启动web模块,使用curl测试所有接口

    扩展挑战:在父POM中覆盖commons-lang3的版本为3.14.0,在common模块中添加一个使用StringUtils.isBlank()的方法,验证版本覆盖是否生效。

    🔗 SpringBoot 4.x 从入门到企业级实战系列课程

    去订阅

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 第2课:SpringBoot4.x 项目构建:Maven/Gradle配置详解,父工程与依赖管理
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!