目錄
Gradle 基礎
什麼是 Gradle?
Gradle 是一個自動化建構工具,主要用於 Java、Kotlin、Android 專案的建構、測試、部署。
核心特性
- 彈性:使用 Groovy 或 Kotlin DSL 編寫建構腳本
- 高效能:增量建構、建構快取、並行執行
- 可擴展:豐富的插件生態系統
- 依賴管理:強大的依賴解析和管理
Gradle vs Maven
| 特性 | Gradle | Maven |
|---|---|---|
| 配置語言 | Groovy/Kotlin DSL(程式化) | XML(聲明式) |
| 建構速度 | 快(增量建構) | 較慢 |
| 彈性 | 高(可程式化) | 低(約定優於配置) |
| 學習曲線 | 較陡 | 較平緩 |
| 生態系統 | Android 官方工具 | Java 傳統標準 |
build.gradle 結構
基本結構(Groovy DSL)
// build.gradle
// 1. 插件
plugins {
id 'java'
id 'application'
}
// 2. 專案資訊
group = 'com.example'
version = '1.0.0'
// 3. 倉庫
repositories {
mavenCentral()
}
// 4. 依賴
dependencies {
implementation 'com.google.guava:guava:31.1-jre'
testImplementation 'junit:junit:4.13.2'
}
// 5. Java 配置
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
// 6. 應用程式配置
application {
mainClass = 'com.example.Main'
}
// 7. 自訂任務
task hello {
doLast {
println 'Hello, Gradle!'
}
}
Kotlin DSL(build.gradle.kts)
// build.gradle.kts
plugins {
java
application
}
group = "com.example"
version = "1.0.0"
repositories {
mavenCentral()
}
dependencies {
implementation("com.google.guava:guava:31.1-jre")
testImplementation("junit:junit:4.13.2")
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
application {
mainClass.set("com.example.Main")
}
tasks.register("hello") {
doLast {
println("Hello, Gradle!")
}
}
依賴管理
依賴配置(Configuration)
常用配置
dependencies {
// 編譯和執行時都需要
implementation 'com.google.guava:guava:31.1-jre'
// 只在編譯時需要(不會打包)
compileOnly 'org.projectlombok:lombok:1.18.26'
// 只在執行時需要
runtimeOnly 'com.h2database:h2:2.1.214'
// API 依賴(會傳遞給消費者)
api 'org.springframework.boot:spring-boot-starter-web:3.0.0'
// 測試依賴
testImplementation 'junit:junit:4.13.2'
testCompileOnly 'org.projectlombok:lombok:1.18.26'
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.9.2'
// 註解處理器
annotationProcessor 'org.projectlombok:lombok:1.18.26'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.26'
}
配置說明
| 配置 | 用途 | 傳遞性 | 範圍 |
|---|---|---|---|
implementation |
內部使用 | ❌ 不傳遞 | 編譯+執行 |
api |
對外暴露 | ✅ 傳遞 | 編譯+執行 |
compileOnly |
只編譯 | ❌ | 編譯 |
runtimeOnly |
只執行 | ❌ | 執行 |
testImplementation |
測試 | ❌ | 測試 |
選擇原則:
- 優先使用
implementation(快速建構) - 只在需要對外暴露時使用
api compileOnly用於 Lombok、註解等runtimeOnly用於資料庫驅動等
依賴版本管理
方式 1:直接指定版本
dependencies {
implementation 'com.google.guava:guava:31.1-jre'
}
方式 2:使用變數
// 在 build.gradle 頂部定義
ext {
guavaVersion = '31.1-jre'
springVersion = '3.0.0'
}
dependencies {
implementation "com.google.guava:guava:${guavaVersion}"
implementation "org.springframework.boot:spring-boot-starter:${springVersion}"
}
方式 3:使用 gradle.properties
# gradle.properties
guavaVersion=31.1-jre
springVersion=3.0.0
// build.gradle
dependencies {
implementation "com.google.guava:guava:${guavaVersion}"
implementation "org.springframework.boot:spring-boot-starter:${springVersion}"
}
方式 4:版本目錄(推薦,Gradle 7.0+)
# gradle/libs.versions.toml
[versions]
guava = "31.1-jre"
spring = "3.0.0"
junit = "5.9.2"
[libraries]
guava = { module = "com.google.guava:guava", version.ref = "guava" }
spring-boot-starter = { module = "org.springframework.boot:spring-boot-starter", version.ref = "spring" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
[bundles]
spring = ["spring-boot-starter", "spring-boot-starter-web"]
[plugins]
spring-boot = { id = "org.springframework.boot", version.ref = "spring" }
// build.gradle
dependencies {
implementation libs.guava
implementation libs.spring.boot.starter
testImplementation libs.junit.jupiter
// 使用 bundle
implementation libs.bundles.spring
}
依賴排除
排除特定依賴
dependencies {
implementation('org.springframework.boot:spring-boot-starter-web:3.0.0') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
}
}
全局排除
configurations {
all {
exclude group: 'org.slf4j', module: 'slf4j-log4j12'
}
}
依賴版本衝突解決
強制使用特定版本
configurations.all {
resolutionStrategy {
// 強制使用特定版本
force 'com.google.guava:guava:31.1-jre'
// 失敗時使用特定版本
failOnVersionConflict()
}
}
查看依賴樹
./gradlew dependencies
# 查看特定配置
./gradlew dependencies --configuration implementation
# 查看依賴衝突
./gradlew dependencyInsight --dependency guava
動態版本
dependencies {
// 使用最新版本(不推薦)
implementation 'com.google.guava:guava:+'
// 使用最新的 31.x 版本
implementation 'com.google.guava:guava:31.+'
// 使用範圍
implementation 'com.google.guava:guava:[31.0,32.0)'
}
⚠️ 注意:動態版本會影響建構的可重現性,生產環境應避免使用。
倉庫配置
預定義倉庫
repositories {
// Maven Central(最常用)
mavenCentral()
// Google Maven Repository(Android)
google()
// JCenter(已棄用)
jcenter()
// Maven Local(本地 .m2)
mavenLocal()
// Gradle Plugin Portal
gradlePluginPortal()
}
自訂 Maven 倉庫
repositories {
maven {
url 'https://repo.spring.io/release'
}
// 需要認證的倉庫
maven {
url 'https://private-repo.example.com/maven'
credentials {
username = project.findProperty('repoUser') ?: 'default'
password = project.findProperty('repoPassword') ?: 'default'
}
}
}
倉庫優先順序
repositories {
// 先搜尋本地倉庫
mavenLocal()
// 再搜尋私有倉庫
maven {
url 'https://private-repo.example.com/maven'
}
// 最後搜尋 Maven Central
mavenCentral()
}
國內鏡像加速
repositories {
// 阿里雲 Maven 鏡像
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
// 或使用騰訊雲
maven { url 'https://mirrors.cloud.tencent.com/nexus/repository/maven-public/' }
}
任務(Tasks)
內建任務
# 建構專案
./gradlew build
# 清理建構目錄
./gradlew clean
# 編譯 Java 程式碼
./gradlew compileJava
# 編譯測試程式碼
./gradlew compileTestJava
# 執行測試
./gradlew test
# 執行應用程式
./gradlew run
# 建立 JAR 檔案
./gradlew jar
# 檢查依賴更新
./gradlew dependencyUpdates
查看所有任務
# 查看所有可用任務
./gradlew tasks
# 查看所有任務(包括隱藏的)
./gradlew tasks --all
# 查看特定群組的任務
./gradlew tasks --group="build"
自訂任務
基本任務
// 簡單任務
task hello {
doLast {
println 'Hello, Gradle!'
}
}
// 帶描述和群組
task hello {
group = 'custom'
description = '顯示歡迎訊息'
doLast {
println 'Hello, Gradle!'
}
}
任務依賴
task taskA {
doLast {
println 'Task A'
}
}
task taskB {
doLast {
println 'Task B'
}
}
// taskC 依賴 taskA 和 taskB
task taskC(dependsOn: [taskA, taskB]) {
doLast {
println 'Task C'
}
}
// 執行 taskC 會先執行 taskA 和 taskB
動態任務
4.times { counter ->
task "task$counter" {
doLast {
println "Task $counter"
}
}
}
任務類型
// Copy 任務
task copyFiles(type: Copy) {
from 'src/main/resources'
into 'build/resources'
}
// Zip 任務
task zipFiles(type: Zip) {
from 'build/libs'
archiveFileName = 'myapp.zip'
destinationDirectory = file('build/distributions')
}
// Exec 任務(執行外部指令)
task runScript(type: Exec) {
commandLine 'sh', 'scripts/deploy.sh'
}
// JavaExec 任務
task runApp(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = 'com.example.Main'
args 'arg1', 'arg2'
}
任務配置
doFirst 和 doLast
task myTask {
doFirst {
println '任務開始前'
}
doLast {
println '任務結束後'
}
}
// 可以鏈式呼叫
task myTask {
doLast {
println 'Step 1'
}
}
myTask.doLast {
println 'Step 2'
}
條件執行
task conditionalTask {
onlyIf {
project.hasProperty('runTask')
}
doLast {
println '條件滿足,執行任務'
}
}
// 使用:./gradlew conditionalTask -PrunTask
插件(Plugins)
核心插件
plugins {
// Java 插件
id 'java'
// Java Library 插件
id 'java-library'
// Application 插件
id 'application'
// Groovy 插件
id 'groovy'
// Kotlin 插件
id 'org.jetbrains.kotlin.jvm' version '1.8.20'
}
Java 插件配置
plugins {
id 'java'
}
java {
// Java 版本
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
// 產生 source jar
withSourcesJar()
// 產生 javadoc jar
withJavadocJar()
}
// 編碼設定
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
// 測試配置
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
Application 插件
plugins {
id 'application'
}
application {
// 主類別
mainClass = 'com.example.Main'
// 應用程式名稱
applicationName = 'myapp'
// JVM 參數
applicationDefaultJvmArgs = ['-Xmx512m', '-Xms256m']
}
// 建立可執行的發布包
distributions {
main {
contents {
from('scripts') {
into 'bin'
}
from('config') {
into 'config'
}
}
}
}
產生的檔案:
./gradlew installDist
# 產生在 build/install/myapp/
./gradlew distZip
# 產生 build/distributions/myapp.zip
Spring Boot 插件
plugins {
id 'org.springframework.boot' version '3.0.0'
id 'io.spring.dependency-management' version '1.1.0'
id 'java'
}
group = 'com.example'
version = '1.0.0'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
// 建構可執行 JAR
bootJar {
archiveFileName = 'app.jar'
}
執行:
./gradlew bootRun
./gradlew bootJar
java -jar build/libs/app.jar
Shadow 插件(Fat JAR)
plugins {
id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'java'
id 'application'
}
application {
mainClass = 'com.example.Main'
}
shadowJar {
archiveFileName = 'app-all.jar'
// 排除特定檔案
exclude 'META-INF/*.SF'
exclude 'META-INF/*.DSA'
exclude 'META-INF/*.RSA'
// 合併 service files
mergeServiceFiles()
}
建構:
./gradlew shadowJar
java -jar build/libs/app-all.jar
社群插件
plugins {
// Lombok
id 'io.freefair.lombok' version '8.0.1'
// JaCoCo(程式碼覆蓋率)
id 'jacoco'
// Spotless(程式碼格式化)
id 'com.diffplug.spotless' version '6.18.0'
// Dependency Updates
id 'com.github.ben-manes.versions' version '0.46.0'
}
// Lombok 配置
lombok {
version = '1.18.26'
}
// JaCoCo 配置
jacoco {
toolVersion = '0.8.9'
}
jacocoTestReport {
reports {
xml.required = true
html.required = true
}
}
// Spotless 配置
spotless {
java {
googleJavaFormat('1.15.0')
removeUnusedImports()
trimTrailingWhitespace()
endWithNewline()
}
}
多專案建構
專案結構
myproject/
├── settings.gradle # 定義子專案
├── build.gradle # 根專案建構腳本
├── gradle.properties # 全局屬性
├── core/
│ ├── build.gradle # core 子專案
│ └── src/
├── api/
│ ├── build.gradle # api 子專案
│ └── src/
└── web/
├── build.gradle # web 子專案
└── src/
settings.gradle
// settings.gradle
rootProject.name = 'myproject'
include 'core'
include 'api'
include 'web'
// 或使用模式匹配
// include ':services:*'
根 build.gradle
// 根專案的 build.gradle
// 所有專案共用配置
allprojects {
group = 'com.example'
version = '1.0.0'
repositories {
mavenCentral()
}
}
// 所有子專案共用配置
subprojects {
apply plugin: 'java'
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependencies {
testImplementation 'junit:junit:4.13.2'
}
}
子專案 build.gradle
// core/build.gradle
plugins {
id 'java-library'
}
dependencies {
api 'com.google.guava:guava:31.1-jre'
implementation 'org.slf4j:slf4j-api:2.0.7'
}
// api/build.gradle
plugins {
id 'java'
}
dependencies {
// 依賴其他子專案
implementation project(':core')
implementation 'org.springframework.boot:spring-boot-starter-web:3.0.0'
}
// web/build.gradle
plugins {
id 'application'
}
application {
mainClass = 'com.example.web.Application'
}
dependencies {
implementation project(':api')
implementation project(':core')
}
建構多專案
# 建構所有子專案
./gradlew build
# 建構特定子專案
./gradlew :core:build
./gradlew :api:build
# 執行特定子專案的任務
./gradlew :web:run
# 查看專案結構
./gradlew projects
Gradle Wrapper
什麼是 Wrapper?
Gradle Wrapper 確保所有開發者使用相同版本的 Gradle,不需要預先安裝 Gradle。
檔案結構
project/
├── gradlew # Unix/Linux/Mac 執行腳本
├── gradlew.bat # Windows 執行腳本
└── gradle/
└── wrapper/
├── gradle-wrapper.jar
└── gradle-wrapper.properties
gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
產生 Wrapper
# 產生 wrapper(需要已安裝 Gradle)
gradle wrapper --gradle-version 8.2
# 指定發布版本
gradle wrapper --gradle-version 8.2 --distribution-type all
更新 Wrapper
# 更新到最新版本
./gradlew wrapper --gradle-version 8.2
使用 Wrapper
# Unix/Linux/Mac
./gradlew build
# Windows
gradlew.bat build
常用指令
建構相關
# 清理建構目錄
./gradlew clean
# 編譯
./gradlew compileJava
# 建構專案
./gradlew build
# 建構並跳過測試
./gradlew build -x test
# 持續建構(檔案變更時自動建構)
./gradlew build --continuous
執行與測試
# 執行應用程式
./gradlew run
# 執行測試
./gradlew test
# 執行特定測試類別
./gradlew test --tests com.example.MyTest
# 執行特定測試方法
./gradlew test --tests com.example.MyTest.testMethod
依賴相關
# 查看依賴樹
./gradlew dependencies
# 查看特定配置的依賴
./gradlew dependencies --configuration implementation
# 查看依賴衝突
./gradlew dependencyInsight --dependency guava
# 檢查依賴更新
./gradlew dependencyUpdates
資訊查詢
# 查看所有任務
./gradlew tasks
# 查看專案結構
./gradlew projects
# 查看專案屬性
./gradlew properties
# 顯示建構掃描
./gradlew build --scan
效能優化
# 使用建構快取
./gradlew build --build-cache
# 並行建構
./gradlew build --parallel
# 顯示建構效能
./gradlew build --profile
# Daemon 管理
./gradlew --status # 查看 daemon 狀態
./gradlew --stop # 停止 daemon
偵錯相關
# 顯示詳細日誌
./gradlew build --info
# 顯示除錯日誌
./gradlew build --debug
# 顯示堆疊追蹤
./gradlew build --stacktrace
# 完整堆疊追蹤
./gradlew build --full-stacktrace
# Dry run(不執行任務)
./gradlew build --dry-run
實際應用範例
範例 1:簡單 Java 應用程式
// build.gradle
plugins {
id 'java'
id 'application'
}
group = 'com.example'
version = '1.0.0'
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.guava:guava:31.1-jre'
implementation 'org.slf4j:slf4j-api:2.0.7'
runtimeOnly 'org.slf4j:slf4j-simple:2.0.7'
testImplementation 'junit:junit:4.13.2'
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
application {
mainClass = 'com.example.Main'
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
範例 2:Spring Boot 應用程式
// build.gradle
plugins {
id 'org.springframework.boot' version '3.0.0'
id 'io.spring.dependency-management' version '1.1.0'
id 'java'
}
group = 'com.example'
version = '1.0.0'
sourceCompatibility = '17'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
bootJar {
archiveFileName = "${project.name}-${project.version}.jar"
}
範例 3:多模組專案
// settings.gradle
rootProject.name = 'myapp'
include 'domain'
include 'application'
include 'infrastructure'
include 'api'
// build.gradle(根專案)
plugins {
id 'java' apply false
id 'org.springframework.boot' version '3.0.0' apply false
id 'io.spring.dependency-management' version '1.1.0' apply false
}
subprojects {
apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '1.0.0'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.junit.jupiter:junit-jupiter'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
}
// domain/build.gradle
dependencies {
// Domain 不依賴其他模組
}
// application/build.gradle
dependencies {
implementation project(':domain')
}
// infrastructure/build.gradle
dependencies {
implementation project(':domain')
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
}
// api/build.gradle
plugins {
id 'org.springframework.boot'
}
dependencies {
implementation project(':application')
implementation project(':infrastructure')
implementation 'org.springframework.boot:spring-boot-starter-web'
}
bootJar {
archiveFileName = 'app.jar'
}
範例 4:自訂建構邏輯
// build.gradle
plugins {
id 'java'
id 'application'
}
group = 'com.example'
version = '1.0.0'
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.guava:guava:31.1-jre'
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
application {
mainClass = 'com.example.Main'
}
// 自訂任務:複製依賴到 lib 目錄
task copyDependencies(type: Copy) {
from configurations.runtimeClasspath
into "$buildDir/libs/dependencies"
}
// 自訂任務:產生版本資訊
task generateVersionFile {
doLast {
def versionFile = file("$buildDir/version.txt")
versionFile.text = "Version: ${project.version}\nBuild Time: ${new Date()}"
}
}
// 建構時執行自訂任務
build.dependsOn(copyDependencies, generateVersionFile)
// 自訂任務:打包發布檔案
task packageRelease(type: Zip) {
from('build/libs') {
include '*.jar'
}
from('build/libs/dependencies') {
into 'lib'
}
from('scripts') {
into 'bin'
}
from('config') {
into 'config'
}
archiveFileName = "${project.name}-${project.version}.zip"
destinationDirectory = file("$buildDir/distributions")
}
packageRelease.dependsOn(build)
使用:
./gradlew packageRelease
# 產生 build/distributions/myapp-1.0.0.zip
最佳實踐
1. 使用 Gradle Wrapper
✅ 推薦:
# 提交到版本控制
git add gradlew gradlew.bat gradle/
❌ 避免:要求開發者自行安裝 Gradle
2. 版本管理
✅ 推薦:使用版本目錄(Gradle 7.0+)
# gradle/libs.versions.toml
[versions]
spring = "3.0.0"
guava = "31.1-jre"
[libraries]
spring-boot-starter = { module = "org.springframework.boot:spring-boot-starter", version.ref = "spring" }
guava = { module = "com.google.guava:guava", version.ref = "guava" }
❌ 避免:在 build.gradle 中硬編碼版本
3. 依賴配置
✅ 推薦:優先使用 implementation
dependencies {
implementation 'com.google.guava:guava:31.1-jre'
api 'org.springframework:spring-core:6.0.0' // 只在必要時使用
}
❌ 避免:過度使用 api(影響建構速度)
4. 多專案組織
✅ 推薦:清晰的模組劃分
project/
├── domain/ # 業務邏輯
├── application/ # 應用服務
├── infrastructure/ # 技術實作
└── api/ # 對外接口
5. 建構效能優化
// gradle.properties
org.gradle.daemon=true # 啟用 daemon
org.gradle.parallel=true # 並行建構
org.gradle.caching=true # 建構快取
org.gradle.configureondemand=true # 按需配置
6. 忽略建構產物
# .gitignore
.gradle/
build/
out/
*.iml
.idea/
7. 持續整合配置
# .github/workflows/build.yml
name: Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Build with Gradle
run: ./gradlew build
- name: Run tests
run: ./gradlew test
疑難排解
1. 清理並重新建構
./gradlew clean build --refresh-dependencies
2. 停止 Gradle Daemon
./gradlew --stop
3. 查看詳細錯誤
./gradlew build --stacktrace --info
4. 依賴衝突
# 查看依賴樹
./gradlew dependencies
# 查看特定依賴
./gradlew dependencyInsight --dependency spring-core
5. 建構快取問題
# 清除建構快取
rm -rf ~/.gradle/caches/
rm -rf .gradle/
總結
核心概念
- 依賴管理:implementation、api、compileOnly、runtimeOnly
- 插件系統:Java、Spring Boot、Application 等
- 任務系統:內建任務、自訂任務、任務依賴
- 多專案建構:模組化、依賴關係
- Gradle Wrapper:版本一致性
常用指令
./gradlew build # 建構
./gradlew test # 測試
./gradlew run # 執行
./gradlew dependencies # 查看依賴
./gradlew tasks # 查看任務
./gradlew clean # 清理
配置要點
- ✅ 使用 Gradle Wrapper
- ✅ 版本集中管理(版本目錄)
- ✅ 優先使用
implementation - ✅ 啟用建構快取和並行建構
- ✅ 清晰的多專案結構
建立日期:2025-11-03