以往 Gradle 只能使用 Groovy 語言撰寫 Script,因為我對 Groovy 不熟,所以大多從 Google 尋找到解法後,再複製貼上稍作修改解決。不過既然現在 Gradle 已經支援 Kotlin DSL,當然就要自己寫 gradle task,整合多個 plugin 使出連續技,釋放 Gradle 強大功能了。
我把專案結構拆分為4個子專案,並參考官方文件 Gradle multi-project builds 進行設定。以下只會擷取重要的程式碼進行說明,如果讀者想進一步了解細節,可以參考 github 完整程式碼
// settings.gradle.kts
rootProject.name = "fanpoll"
include("app", "infra", "projects:ops", "projects:club")
buildSrc 則是把多個專案的共用邏輯集中寫為 convention plugin。
因為專案需要 kotlin serialization compiler plugin 及 shadow plugin,所以必須要在 buildSrc/build.gradle.kts 的 repositories 加上 gradlePluginPortal() 才能下載 plugin
plugins {
`kotlin-dsl`
}
repositories {
// Use the plugin portal to apply community plugins in convention plugins.
gradlePluginPortal()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30")
implementation("org.jetbrains.kotlin:kotlin-serialization:1.5.30")
implementation("gradle.plugin.com.github.jengelman.gradle.plugins:shadow:7.0.0")
}
為了避免 shadow plugin 在打包 ops, club 子專案的時候,把 infra 專案的 transitive dependencies 的 jar 檔也打包進去,造成 fat jar 的大小爆增,所以我在 subproject-conventions 設定 shadowJar task 的 configuration 的 isCanBeResolved 為 ture,再設定 transitive 屬性為 false。
plugins {
id("app.java-conventions")
}
dependencies {
implementation(project(":infra"))
}
configurations.api.get().isCanBeResolved = true
tasks.shadowJar {
configurations = listOf(project.configurations.api.get().setTransitive(false))
dependencies {
exclude(dependency("org.jetbrains.kotlin:kotlin-stdlib:.*"))
exclude(dependency("org.jetbrains.kotlin:kotlin-stdlib-jdk8:.*"))
}
}
最後在 app/build.gradle.kts 設定 project dependenies,而且要設定 configuration = "shadow",shadow plugin 才會把這些子專案合併為1個 fat jar 檔
dependencies {
implementation(project(":infra", configuration = "shadow"))
implementation(project(":projects:ops", configuration = "shadow"))
implementation(project(":projects:club", configuration = "shadow"))
}
除了程式碼之外,設定檔…等其它檔案也要一起打包。但是設定檔必須要考慮到不同部署環境的設定值可能會不同,例如 dev, staging, prod 的效能參數會根據硬體規格調整。如果你的 git branch 與部署環境存在著對應關係的話,那麼可以寫 gradle task 搭配 Git Plugin 自動依據當下的 git branch 打包對應的設定檔或其它檔案
雖然 Shadow Plugin 已經打包成1個 jar 檔,直接下 java -jar 指令就可以執行了,不過因為本專案有搭配 PostgreSQL 與 Redis,所以用 Docker Compose 搞定一切會比較方便。
因為有使用 shadow,所以要注意 Dockerfile 裡面的 build 目錄要改為 build/install/app-shadow/
FROM openjdk:11-jre-slim
EXPOSE 8080:8080
RUN mkdir /app
COPY ./build/install/app-shadow/ /app/
WORKDIR /app/bin
CMD ["./app"]
至於 docker-compose.yml 就不貼上來了,請讀者自行到 github 查看
如果想要對 Docker image 及 container 做更多細節的客製化操作,可以使用 Gradle Docker Plugin
最後推薦一下 IntelliJ IDEA 的 docker plugin,在本地端開發時,就能透過 IDE 查看及操作 docker container 非常方便,而且最新版 2021.2 版本又進一步優化,有更多 docker 參數可以設定。
這兩天已經說明了如何實作多專案模組化的開發、建置、打包、部署,明天會開始進入 Ktor 的世界,整合第三方框架及函式庫,還有實作 Ktor 缺少的套件來強化 Ktor。