Spring Boot 项目如何配置 Maven 才不容易踩坑?
Spring Boot 项目里,Maven 的核心作用不是“把依赖写进 pom.xml”这么简单,而是把 Java 版本、依赖版本、打包方式、测试插件和运行命令统一到一套可重复的构建规则里。最省心的做法是使用 spring-boot-starter-parent,让 Spring Boot 帮你管理常见依赖和插件版本;如果公司已有统一父 POM,则用 spring-boot-dependencies 做 dependencyManagement。两种方式都能用,区别在于谁来当父 POM,这一点在企业项目里经常被忽略。
Spring Boot 和 Maven 集成的关键配置
典型单体服务可以直接继承 Spring Boot 父 POM:
xml<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.5</version> <relativePath/> </parent> <properties> <java.version>17</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
如果不能继承它,比如公司已经有 company-parent,就导入 BOM:
xml<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.2.5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
打包可执行 JAR 时必须关注 spring-boot-maven-plugin。它会把普通 JAR 重新打成包含启动器和依赖布局的 Boot JAR,否则 java -jar 可能找不到主类或依赖。
xml<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.example.Application</mainClass> </configuration> <executions> <execution> <goals><goal>repackage</goal></goals> </execution> </executions> </plugin> </plugins> </build>
常用命令保持简单即可:mvn clean package 打包,mvn spring-boot:run 本地启动,java -jar target/app.jar --spring.profiles.active=prod 按环境运行。Profile 可以放在 Maven 里控制构建参数,但业务环境更推荐由 Spring 的 application-dev.yml、环境变量或启动参数控制,避免“构建一次只能用于一个环境”。
还有一个容易被忽略的点是测试插件。Spring Boot 父 POM 会管理 Surefire 和 Failsafe 的常见版本,但如果企业父 POM 覆盖过这些插件,JUnit 5 测试可能出现本地能跑、CI 不识别测试用例的情况。排查时不要只看依赖,先执行 mvn -X test 或检查 target/surefire-reports 是否生成。对于多模块项目,建议在父 POM 统一插件版本,在应用模块只保留差异化配置。
资源过滤也要克制使用。src/main/resources 全量开启 filtering 后,某些二进制资源、证书文件或包含 ${} 的配置文件可能被 Maven 意外处理。更稳妥的方式是只对 application-build.properties 这类构建信息文件开启过滤,把运行环境变量留给 Spring Boot 自己解析。这样构建产物更通用,也更适合 Docker 镜像和多环境部署。
版本升级时也要先读 Spring Boot 的 release notes。比如从 2.x 升到 3.x,Java 基线、Jakarta 包名、部分自动配置条件都会变化,单靠 Maven 改版本并不能完成迁移。稳妥做法是先在分支里升级父 POM 或 BOM,再跑完整测试和依赖树检查,最后处理运行时配置差异。
追问
starter-parent 和 spring-boot-dependencies 应该选哪个?
starter-parent 适合新项目或没有统一父 POM 的项目,因为它连插件默认配置也一起给了。spring-boot-dependencies 更适合企业项目,它只导入依赖版本,不抢父 POM 位置。取舍点在于控制权:前者省配置,后者更容易接入公司构建规范。常见坑是导入 BOM 后以为插件版本也被完整管理了,结果编译器插件、surefire 插件仍受公司父 POM 或 Maven 默认值影响。
为什么不建议给每个 starter 手动写 version?
Spring Boot 的 BOM 已经验证过一组依赖组合,手动写版本会破坏这套兼容矩阵。偶尔覆盖版本可以解决安全漏洞,但要配合 mvn dependency:tree 看传递依赖有没有被带歪。边界是底层库存在紧急 CVE 或公司必须统一版本时才覆盖,而不是为了“用最新版”。踩坑最多的是 Jackson、Tomcat、Hibernate 这类深度集成依赖,单独升级后编译通过,运行时才暴露方法签名不兼容。
Maven Profile 能不能直接管理 Spring 的 dev、prod 环境?
可以,但不建议把它当唯一方案。Maven Profile 更适合控制构建时差异,比如是否开启资源过滤、是否打包前端资源、是否替换构建号。Spring Profile 更适合控制运行时差异,比如数据库、缓存、日志级别。坑在于把生产配置过滤进 JAR,后面同一个包就没法在不同环境复用,也容易把敏感信息带进构建产物。
spring-boot-maven-plugin 和 maven-jar-plugin 有什么区别?
maven-jar-plugin 生成普通 JAR,主要负责 manifest、包含文件等基础打包。spring-boot-maven-plugin 的 repackage 会把普通 JAR 改造成可执行 Boot JAR,并处理依赖嵌套和启动类。取舍上,库项目不应该打 Boot JAR,应用项目才需要。常见坑是多模块项目在公共模块也启用 repackage,导致公共模块无法作为普通依赖被其他模块正确引用。
CI 里构建 Spring Boot 项目有哪些必要检查?
至少要跑 mvn -B clean verify,让测试、编译和打包在非交互模式下完成。可以加 mvn dependency:tree 或 OWASP Dependency Check 做依赖排查,但安全扫描会增加耗时,适合按分支或定时任务分层执行。CI 中最好固定 JDK 和 Maven 版本,否则本地能过、流水线失败很难排查。一个常见坑是 CI 使用 JDK 21,本地使用 JDK 17,Spring Boot 版本、插件版本和字节码目标不一致时会出现莫名其妙的编译或测试问题。
小结
Spring Boot 与 Maven 集成的重点是版本统一、插件正确、环境边界清晰。新项目优先用 starter-parent,企业项目用 BOM 接入现有父 POM;应用模块启用 spring-boot-maven-plugin,公共模块保持普通 JAR。只要这几条边界守住,后面的依赖升级、打包和 CI 才不会变成反复试错。