14、Spring Boot项目:Spring Boot插件、BootJar与BootRun、分层Jar、依赖管理
Spring Boot 项目用 Gradle 来构建,说实话,是我这几年用得最顺手的组合。你想想看,Spring Boot 本身已经帮我们省去了大量配置,再加上 Gradle 的灵活性和性能,简直就是绝配。
今天我们就来聊聊 Spring Boot 项目在 Gradle 中的几个核心玩法。我会结合我自己的踩坑经历,把 Spring Boot 插件、BootJar、BootRun、分层 Jar 以及依赖管理这些知识点,掰开了揉碎了讲给你听。
14.1 Spring Boot Gradle 插件:项目的基石
首先,你得引入 Spring Boot 的 Gradle 插件。这个插件是官方提供的,专门用来打包和运行 Spring Boot 应用。
// build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.0'
id 'io.spring.dependency-management' version '1.1.4'
}
这里有两个插件:org.springframework.boot 和 io.spring.dependency-management。前者负责打包和运行,后者负责管理依赖版本。我个人习惯把这两个插件一起引入,省心。
核心作用:
org.springframework.boot:提供bootJar、bootRun等任务io.spring.dependency-management:自动管理 Spring Boot 及其相关依赖的版本
为什么需要依赖管理插件?因为 Spring Boot 的依赖版本是经过严格测试的。你想想看,如果自己手动指定版本,很容易出现版本冲突。这个插件会自动从 Spring Boot BOM(Bill of Materials)中读取版本号,你只需要声明依赖名,不用写版本号。
14.2 BootJar:打包可执行 Jar
Spring Boot 应用最终要部署,通常是一个可执行的 Jar 包。这个 Jar 包和普通的 Jar 不一样,它里面包含了所有依赖的库,以及一个特殊的启动器。
// 执行打包命令
./gradlew bootJar
// 打包后的文件位置
build/libs/myapp-0.0.1-SNAPSHOT.jar
这个 Jar 包可以直接用 java -jar 运行。我记得第一次用的时候,觉得特别神奇——一个 Jar 包就能跑起整个 Web 应用。
BootJar 任务默认会生成一个 fat jar(胖 Jar),把所有依赖都打进去。但有时候,你可能会遇到一些特殊情况。比如,有些依赖在打包时需要排除掉。
// 排除特定依赖
bootJar {
exclude("com.example:unwanted-lib")
}
注意:我曾经遇到过一个问题:打包后的 Jar 包体积过大,导致部署时传输很慢。后来发现是因为把测试依赖也打进去了。解决办法是在 dependencies 块中正确使用 testImplementation 而不是 implementation。
14.3 BootRun:开发时的好帮手
开发阶段,我们不可能每次都打包再运行。BootRun 任务就是为此设计的。它可以直接从源码启动 Spring Boot 应用,支持热重载。
// 启动应用
./gradlew bootRun
// 也可以配置 JVM 参数
bootRun {
jvmArgs = ['-Dspring.profiles.active=dev', '-Xmx512m']
}
BootRun 默认会使用 src/main/resources 下的配置文件。如果你需要切换环境,可以通过 --args 参数传递。
./gradlew bootRun --args='--spring.profiles.active=prod'
嗯,这里要注意一点:BootRun 默认不会自动重新加载静态资源。如果你修改了 HTML 或 CSS 文件,需要配合 DevTools 才能实现热更新。
小技巧:我习惯在开发时开启 spring.devtools.restart.enabled=true,这样修改 Java 代码后,应用会自动重启。虽然重启需要几秒钟,但比手动停止再启动快多了。
14.4 分层 Jar:优化镜像构建
说到分层 Jar,这其实是 Spring Boot 2.3 之后引入的一个特性。说白了,就是把 Jar 包里的内容分成多个层(layer),这样在构建 Docker 镜像时,可以充分利用缓存。
为什么要分层?你想想看,传统的 fat jar 里,所有依赖和业务代码都混在一起。每次修改一行代码,整个 Jar 包都得重新构建,Docker 镜像也得重新下载所有层。而分层 Jar 把不常变化的依赖(比如 Spring 框架、第三方库)放在一层,把经常变化的业务代码放在另一层。
// 启用分层 Jar
bootJar {
layered {
application {
// 业务代码层
into("BOOT-INF/classes") {
include("com/example/**")
}
}
dependencies {
// 依赖层
into("BOOT-INF/lib") {
exclude("com/example/**")
}
}
}
}
构建完成后,你可以用以下命令查看分层结构:
java -Djarmode=layertools -jar myapp.jar list
输出结果类似这样:
dependencies
spring-boot-loader
snapshot-dependencies
application
在 Dockerfile 中,你可以这样利用分层:
FROM eclipse-temurin:17-jre as builder
WORKDIR /app
COPY myapp.jar .
RUN java -Djarmode=layertools -jar myapp.jar extract
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
这样,当你只修改了业务代码时,Docker 只需要重新构建 application 层,依赖层可以直接使用缓存。构建速度能提升 80% 以上。
避坑指南:我曾经在分层 Jar 上踩过一个坑:如果依赖中有 SNAPSHOT 版本,它会被单独放在 snapshot-dependencies 层。但 SNAPSHOT 版本经常变化,导致缓存失效。后来我建议团队在发布前,把所有 SNAPSHOT 依赖都替换成正式版本。
14.5 依赖管理:版本统一的艺术
Spring Boot 项目的依赖管理,核心就是版本统一。你想想看,一个项目可能有几十个依赖,如果每个依赖都手动指定版本,很容易出现版本冲突。
Gradle 的依赖管理插件,会自动从 Spring Boot BOM 中读取版本号。你只需要声明依赖名:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'mysql:mysql-connector-java'
// 不需要写版本号
}
但有时候,你可能需要覆盖某个依赖的版本。比如,项目需要用到 MySQL 8.0.33,但 BOM 中默认是 8.0.30。
// 覆盖版本
ext {
set('mysql.version', '8.0.33')
}
或者,你也可以在 dependencyManagement 块中显式指定:
dependencyManagement {
imports {
mavenBom "org.springframework.boot:spring-boot-dependencies:3.2.0"
}
dependencies {
dependency 'mysql:mysql-connector-java:8.0.33'
}
}
个人经验:我建议尽量少覆盖 BOM 中的版本。除非有明确的兼容性测试,否则盲目升级依赖版本,可能会引入不兼容的问题。我曾经因为升级了 Jackson 版本,导致 JSON 序列化行为发生变化,排查了半天。
另外,Gradle 还支持 constraints 来强制版本:
dependencies {
constraints {
implementation('com.fasterxml.jackson.core:jackson-databind:2.15.3') {
because '修复了安全漏洞'
}
}
}
这种方式的好处是,你可以为所有传递依赖也强制指定版本。
14.6 知识体系总览
为了让你更直观地理解本章的知识结构,我画了一张图:
这张图展示了 Spring Boot 项目在 Gradle 中的四个核心维度。插件是基础,BootJar 和 BootRun 是日常操作,分层 Jar 是进阶优化,依赖管理是长期维护的关键。
好了,这一章的内容就到这里。希望这些经验能帮你少走一些弯路。如果你在实际项目中遇到什么问题,欢迎随时交流。
公众号:蓝海资料掘金营,微信deep3321