工具鏈準備
- JDK 11/17/21
- Gradle 7.6+ (Wrapper 推薦)
- Eclipse + Buildship 插件 / IntelliJ IDEA (內建支援)
- Jenkins 2.400+ + Git + Maven/Gradle 插件
Eclipse Buildship 整合
- 安裝:Help → Eclipse Marketplace → 搜尋 Buildship → Install。
- 可選:Minimalist Gradle Editor 提供
build.gradle 語法高亮。
建立 Gradle Java Web 專案
方式一:IDE 建立 (Buildship)
- File → New → Other → Gradle → Gradle Project
- 專案名、位置、選擇本地 Gradle 發佈版
- Finish → 觸發
gradle init --type java-library
- 轉為 Web 專案:專案右鍵 → Properties → Project Facets → 勾選 Dynamic Web Module、Java、JavaScript
方式二:手動建立標準目錄結構
my-webapp/
├── build.gradle
├── settings.gradle
├── src/
│ ├── main/
│ │ ├── java/com/example/
│ │ ├── resources/ # 配置檔、Mapper XML
│ │ └── webapp/ # Web 資源
│ │ ├── WEB-INF/web.xml
│ │ ├── static/ # CSS/JS/圖片
│ │ └── index.jsp
│ └── test/
│ ├── java/
│ └── resources/
└── gradle/wrapper/ # Gradle Wrapper
核心 build.gradle 配置
plugins {
id 'war' // WAR 打包
id 'org.akhikhl.gretty' version '3.0.2' // 內嵌容器運行/測試
id 'org.hidetake.ssh' version '2.10.0' // SSH 部署
}
group = 'com.example'
version = '1.0.0-SNAPSHOT'
description = 'Gradle Java Web Demo'
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
repositories {
mavenLocal()
mavenCentral()
maven { url 'https://maven.aliyun.com/repository/public' } // 國內鏡像
gradlePluginPortal()
}
dependencies {
// Servlet API (providedCompile 在 Gradle 7+ 為 compileOnly)
compileOnly 'jakarta.servlet:jakarta.servlet-api:5.0.0'
// 或舊版 javax.servlet:javax.servlet-api:4.0.1
// Spring MVC
implementation 'org.springframework:spring-webmvc:5.3.30'
implementation 'org.springframework:spring-jdbc:5.3.30'
implementation 'org.springframework:spring-tx:5.3.30'
implementation 'org.springframework:spring-context-support:5.3.30'
// MyBatis
implementation 'org.mybatis:mybatis:3.5.13'
implementation 'org.mybatis:mybatis-spring:2.0.7'
// 連線池
implementation 'com.alibaba:druid:1.2.20'
// JSON
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
// 日誌
implementation 'org.slf4j:slf4j-api:2.0.9'
implementation 'ch.qos.logback:logback-classic:1.4.11'
// 測試
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1'
testImplementation 'org.mockito:mockito-core:5.5.1'
testImplementation 'org.springframework:spring-test:5.3.30'
}
gretty {
servletContainer = 'tomcat9' // 或 jetty9
contextPath = '/myapp'
httpPort = 8080
httpsPort = 8443
fastReload = true // 類變更熱重載
reloadOnClassChange = true
enableNaming = true // JNDI 支援
managedClassReload = true // 受管類重載
}
war {
archiveFileName = "${project.name}-${version}.war"
// 包含額外資源 (如本地 lib 目錄)
from('src/main/webapp') { into 'WEB-INF' }
// from('lib') { into 'WEB-INF/lib' } // 若有本地 JAR
}
tasks.withType(Test) {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
}
}
自定義任務:關機、清理、複製、啟動 (SSH 部署)
// 關閉遠端 Tomcat
task shutdownTomcat {
doLast {
ssh.run {
session(remotes.deployServer) {
println 'Shutting down Tomcat...'
executeScript '''#!/bin/bash
cd /opt/tomcat/bin
./shutdown.sh
sleep 5
# 強制殺殘留進程
pkill -f "tomcat"
'''
}
}
}
}
// 清理舊部署
task cleanDeploy(dependsOn: shutdownTomcat) {
doLast {
ssh.run {
session(remotes.deployServer) {
println 'Cleaning old deployment...'
executeScript '''#!/bin/bash
rm -rf /opt/tomcat/webapps/myapp
rm -f /opt/tomcat/webapps/myapp.war
'''
}
}
}
}
// 複製 WAR
task copyWar(dependsOn: cleanDeploy) {
doLast {
ssh.run {
session(remotes.deployServer) {
println 'Copying WAR...'
put from: "build/libs/${war.archiveFileName.get()}", into: '/opt/tomcat/webapps'
}
}
}
}
// 啟動 Tomcat
task startTomcat(dependsOn: copyWar) {
doLast {
ssh.run {
session(remotes.deployServer) {
println 'Starting Tomcat...'
execute '/opt/tomcat/bin/startup.sh'
}
}
}
}
// 一鍵部署
task deploy(dependsOn: startTomcat) {
group = 'Deployment'
description = 'Full deployment pipeline: shutdown → clean → copy → start'
}
// SSH 配置
ssh.settings {
knownHosts = allowAnyHosts // 僅測試環境,生產請配置 known_hosts
}
remotes {
deployServer {
host = '192.168.1.100'
user = 'deploy'
password = 'deploy123' // 建議使用 SSH Key
identity = file('~/.ssh/id_rsa') // 金鑰路徑
}
}
執行 Gradle 任務
- IDE:Gradle 任務視圖 → 雙擊任務 (如
war、gretty:run、deploy)
- 命令列:
./gradlew war、./gradlew gretty:run、./gradlew deploy
- Wrapper 確保團隊版本一致:
./gradlew wrapper --gradle-version 8.5
Jenkins Pipeline 配置
1. 安裝插件
- Pipeline / Pipeline Stage View
- Git / GitHub / GitLab
- Gradle (或使用 Wrapper 無需安裝)
- SSH Agent / Publish Over SSH (部署用)
2. Jenkinsfile (宣告式 Pipeline)
pipeline {
agent any
tools {
// 若 Jenkins 全域配置了 Gradle,可指定版本名
// gradle 'Gradle 8.5'
}
environment {
GRADLE_OPTS = '-Dorg.gradle.daemon=false -Dorg.gradle.parallel=true'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build & Test') {
steps {
sh './gradlew clean test --info'
}
post {
always {
junit 'build/test-results/test/**/*.xml'
}
}
}
stage('Package WAR') {
steps {
sh './gradlew war -x test'
archiveArtifacts artifacts: 'build/libs/*.war', fingerprint: true
}
}
stage('Deploy Staging') {
when { branch 'develop' }
steps {
sh './gradlew deploy -Penv=staging'
}
}
stage('Deploy Production') {
when { branch 'main' }
steps {
input message: 'Confirm production deployment?', ok: 'Deploy'
sh './gradlew deploy -Penv=prod'
}
}
}
post {
always {
cleanWs()
}
failure {
mail to: 'team@example.com',
subject: "Build Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "Check console: ${env.BUILD_URL}"
}
}
}
3. 多環境參數化
// build.gradle 讀取 -Penv
def env = project.hasProperty('env') ? project.env : 'dev'
def deployConfig = [
dev: [host: '192.168.1.10', user: 'dev', key: '~/.ssh/id_rsa_dev'],
staging: [host: '192.168.1.20', user: 'stage', key: '~/.ssh/id_rsa_stage'],
prod: [host: '192.168.1.30', user: 'prod', key: '~/.ssh/id_rsa_prod']
][env]
remotes {
deployServer {
host = deployConfig.host
user = deployConfig.user
identity = file(deployConfig.key)
}
}
進階優化技巧
- 配置快取:
gradle.properties 設 org.gradle.caching=true、org.gradle.parallel=true、org.gradle.daemon=true (本地開發)。
- 依賴鎖定:
./gradlew dependencies --write-locks 生成 gradle.lockfile,提交版控確保可重現構建。
- 複合建構:
settings.gradle includeBuild('../shared-lib') 替代發布快照版。
- 測試分類:
test { useJUnitPlatform { includeTags 'unit' } }、task integrationTest(type: Test) { useJUnitPlatform { includeTags 'integration' } }。
- Docker 整合:
plugins { id 'com.bmuschko.docker-remote-api' } 構建鏡像推送 Registry,Jenkins 部署改為 kubectl apply / docker stack deploy。
常見問題排查
| 問題 | 解決 |
| Gretty 熱重載不生效 | 確認 fastReload=true、managedClassReload=true、IDE 啟用 Build Project Automatically |
| WAR 缺少依賴 | 檢查 compileOnly vs implementation、war { classpath = configurations.runtimeClasspath } |
| Jenkins SSH 連線失敗 | 憑證管理 → SSH Username with private key、Known hosts 驗證策略、防火牆/安全組放行 22 |
| Gradle Daemon 記憶體溢出 | gradle.properties 加 org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m |
| 多模組測試報告合併 | 根 build.gradle 加 test { reports { html.enabled = true } }、Jenkins junit '**/build/test-results/**/*.xml' |