30、综合实战:构建一个完整微服务项目
说实话,到了第三十讲,咱们终于要干一票大的了。前面二十九章,咱们把 Gradle 的边边角角都摸了一遍,从单模块到多模块,从构建脚本到依赖管理,再到 Docker 镜像和 CI/CD。今天这一章,就是把所有零件拧到一起,造一台能跑的车。
我个人习惯,每次学一个新工具,最后一定要做一个“能看见”的东西。光看理论,记不住。你想想看,一个微服务项目,从代码写到镜像,再到自动部署,这一套流程走下来,你对 Gradle 的理解绝对会上一个台阶。
30.1 项目整体结构设计
咱们要构建的是一个电商系统的订单服务。麻雀虽小,五脏俱全。我把它拆成四个模块:
- order-api:暴露 REST 接口,Spring Boot 启动类放这里。
- order-service:业务逻辑层,处理订单核心逻辑。
- order-dao:数据访问层,跟数据库打交道。
- order-common:公共工具类、DTO、异常定义。
为什么这么拆?我在项目中遇到过,如果所有代码都塞在一个模块里,后期维护就是一场噩梦。尤其是多人协作时,改一个接口可能影响整个服务。拆开之后,每个模块职责清晰,依赖关系也一目了然。
核心原则:模块之间单向依赖。order-api 依赖 order-service,order-service 依赖 order-dao,order-common 被所有模块依赖。千万别搞循环依赖,Gradle 虽然能检测,但跑起来性能会打折扣。
下面这张图,是我用 SVG 画的模块依赖关系,你看一眼就明白了。
30.2 根项目 settings.gradle 配置
多模块项目的起点,就是 settings.gradle。这里有个坑,我曾经漏写了一行,结果子模块死活找不到。你记住,模块名和目录名要一致,不然 Gradle 会迷路。
// settings.gradle
rootProject.name = 'order-platform'
include 'order-common'
include 'order-dao'
include 'order-service'
include 'order-api'
// 如果你有独立的构建缓存配置,也可以加在这里
buildCache {
local {
enabled = true
}
}
30.3 根项目 build.gradle 统一管理
我建议把所有公共配置抽到根项目的 build.gradle 里。比如插件版本、Java 版本、仓库地址。子模块只需要写自己特有的部分。这样做的好处是,升级 Spring Boot 版本时,改一个地方就行。
// 根项目 build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.0' apply false
id 'io.spring.dependency-management' version '1.1.4' apply false
}
allprojects {
group = 'com.example.order'
version = '1.0.0-SNAPSHOT'
repositories {
mavenCentral()
}
}
subprojects {
apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependencies {
// 公共依赖,比如 lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
}
tasks.withType(Test) {
useJUnitPlatform()
}
}
小技巧:用 apply false 声明插件,然后在子模块里按需应用。这样不会把 Spring Boot 插件强加到所有模块上,比如 order-common 就不需要打成可执行 jar。
30.4 子模块专属配置
每个子模块的 build.gradle 都很精简。以 order-api 为例:
// order-api/build.gradle
plugins {
id 'org.springframework.boot'
}
dependencies {
implementation project(':order-service')
implementation 'org.springframework.boot:spring-boot-starter-web'
// 其他运行时依赖
}
// 配置 Spring Boot 的 main 类
bootJar {
mainClass = 'com.example.order.api.OrderApiApplication'
}
order-service 和 order-dao 类似,只依赖自己需要的模块。order-common 最干净,甚至不需要 Spring Boot 插件。
30.5 构建 Docker 镜像
微服务最终要跑在容器里。Gradle 官方推荐用 com.google.cloud.tools.jib 插件,它不需要 Docker 守护进程,直接生成镜像。我个人觉得比 Dockerfile 方便,尤其是 CI 环境里。
// 在 order-api/build.gradle 里添加
plugins {
id 'com.google.cloud.tools.jib' version '3.4.0'
}
jib {
from {
image = 'eclipse-temurin:17-jre-alpine'
}
to {
image = "registry.example.com/order-api:${project.version}"
// 如果私有仓库需要认证,可以配置 auth
}
container {
mainClass = 'com.example.order.api.OrderApiApplication'
ports = ['8080']
}
}
执行 ./gradlew :order-api:jib,镜像就构建并推送到了仓库。嗯,这里要注意,第一次跑会下载基础镜像,有点慢,但后面就快了。
30.6 CI/CD 流水线配置
咱们用 GitHub Actions 做演示。流水线分三步:编译、测试、构建镜像并推送。我曾经见过有人把测试和构建写在一个 job 里,结果测试失败镜像也推上去了,那叫一个乱。
# .github/workflows/ci-cd.yml
name: Order Platform CI/CD
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Build and Test
run: ./gradlew build
- name: Build Docker Image
run: ./gradlew :order-api:jib
env:
DOCKER_REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
DOCKER_REGISTRY_PASS: ${{ secrets.REGISTRY_PASS }}
避坑指南:千万不要把镜像仓库的密码写在代码里。用 GitHub Secrets 或者环境变量注入。我曾经有一次不小心把密码提交到了公开仓库,十分钟后收到一堆机器人登录警告……从那以后,我养成了每次提交前检查敏感信息的习惯。
30.7 多模块构建的常见问题
最后,我总结几个实战中容易踩的坑:
- 依赖版本冲突:子模块引用了不同版本的传递依赖。解决办法是在根项目用
dependencyManagement统一声明版本。 - 构建缓存失效:如果你改了公共模块的代码,但其他模块没重新编译。试试
--rerun-tasks或者清理.gradle缓存。 - Docker 镜像层缓存:Jib 默认会利用层缓存,但如果你的依赖经常变,可以考虑把
jib.container.creationTime设为USE_TIMESTAMPS来避免不必要的层重建。
好了,这一章的内容就到这里。你跟着把项目搭起来,跑一遍流水线,应该就能体会到 Gradle 在微服务构建中的威力。说白了,工具是死的,流程是活的,多动手才能变成自己的经验。