VS Code + GitHub Copilot 開發 Java Web 應用程式教學手冊(3)
版本:v3.0(2026-06-30)
前版:v2.0(2026-03-27)
適用對象:初學者 / 中階工程師 / 企業團隊
技術棧:VS Code 1.126(2026-06-24 發布)· GitHub Copilot(含 Chat、Autopilot、Agent Mode、Agents Window、CLI Agent、Cloud Agent、Research Agent)· Java 21+ · Spring Boot 3.4.x · Maven
定位:企業標準技術白皮書 — 可直接用於專案團隊內部開發規範文件
變更說明:根據 VS Code 1.123–1.126 Release Notes、Agents Window、Autopilot、MCP OAuth、1M Context Window 等重大更新全面改版
目錄
- 1. 總覽
- 2. 開發環境安裝與設定
- 3. 建立第一個 Spring Boot 專案
- 4. GitHub Copilot 實戰應用
- 5. 專案架構設計(企業級)
- 6. 安全性與最佳實務
- 7. 測試與除錯
- 8. CI/CD 與版本控管
- 9. 系統維護與升級
- 10. 團隊導入建議(企業級)
- 11. 常見問題與最佳解法(FAQ)
- 12. 附錄
- 13. 檢查清單(Checklist)
1. 總覽
1.1 VS Code 在 Java 開發的優勢
VS Code 從輕量級編輯器發展至今,已成為企業級 Java 開發的主流選擇之一。主要優勢:
| 面向 | 說明 |
|---|---|
| 啟動速度 | 秒級啟動,記憶體佔用約 300-500 MB(IntelliJ 通常 2-4 GB) |
| AI 原生整合 | GitHub Copilot 深度整合,支援 Chat、Autopilot、Agent Mode、Agents Window |
| 跨語言支援 | 同一 IDE 處理 Java、TypeScript、SQL、YAML、Dockerfile |
| 免費授權 | 核心功能完全免費,企業部署成本低 |
| 擴展生態 | 超過 50,000 個 Extension,可依需求組合 |
| 遠端開發 | 原生支援 Remote SSH、Dev Containers、GitHub Codespaces |
| 終端整合 | 內建終端,Maven/Gradle 指令一鍵執行 |
| 整合式瀏覽器 | 內建瀏覽器支援網頁搜尋、瀏覽歷史、頁面收藏、遠端 Proxy 連線 |
| Agent 原生架構 | 支援 Local / Background / Cloud / CLI / Research Agent,Autopilot 自主決策 |
| Agents Window | 專用 Agent 視窗,可多 Session 並行、跨機器同步、成本追蹤 |
| 1M Context Window | 支援 100 萬 Token 上下文視窗(Claude Opus 4.7、GPT-5.5) |
| 成本透明 | Session 級別費用追蹤,清楚掌握 AI 使用成本 |
開發流程全景圖
flowchart LR
A[需求分析] --> B[架構設計]
B --> C[VS Code 開發]
C --> D[Copilot 輔助]
D --> E[單元測試]
E --> F[Code Review]
F --> G[CI/CD]
G --> H[部署上線]
D -->|自動補全| C
D -->|生成測試| E
D -->|產生 Pipeline| G
style D fill:#6366f1,color:#fff1.2 GitHub Copilot 在開發流程中的角色
GitHub Copilot 不僅是「程式碼自動補全工具」,它在整個 SDLC 中扮演多重角色:
graph TB
subgraph "Copilot 在 SDLC 中的角色"
A[📋 需求分析] -->|Chat: 釐清需求| B[🏗️ 架構設計]
B -->|Chat: 設計討論| C[💻 編碼開發]
C -->|Inline: 自動補全| D[🧪 測試撰寫]
D -->|Chat: 生成測試| E[🔍 Code Review]
E -->|Chat: 審查建議| F[🚀 CI/CD]
F -->|Chat: Pipeline 生成| G[📊 維運監控]
end
style C fill:#22c55e,color:#fff
style D fill:#22c55e,color:#fffCopilot 核心模式(VS Code 1.126 版,2026-06-24):
| 模式 | 說明 | 適用場景 |
|---|---|---|
| Inline Completion | 逐行/逐段自動補全,含 Next Edit Suggestions(NES) | 日常編碼、快速實作 |
| Inline Chat | Ctrl+I 行內對話,不離開編輯器 | 局部重構、快速修復、解釋 |
| Copilot Chat | 側邊欄對話式互動、問答、解釋 | 架構討論、除錯、學習 |
| Agent Mode(Local) | 自主完成多步驟任務,編輯檔案、執行指令、自我修正 | 重構、建立新模組、複雜修改 |
| Autopilot(Preview) | Agent 自主決策完成時機,無需逐步確認,最多迭代三次 | 全自動任務執行 |
| Agent Mode(Background) | 在背景自主執行任務,不阻斷開發 | 長時間任務、自動化 |
| Agent Mode(Cloud) | 在雲端執行,完成後開 PR | 跨時區協作、大規模重構 |
| Research Agent | 深度研究代理,產出結構化 Markdown 報告(唯讀) | 技術調研、API 理解、方案比較 |
| Plan Agent | 將任務拆解為結構化實作計畫,再交給實作 Agent 執行 | 需求分析、架構規劃 |
| Copilot CLI | 終端命令行 Agent,支援 MCP Server | Git 操作、Maven 指令、部署腳本 |
| Smart Actions | 預定義 AI 動作:生成 Commit Message、重命名、修錯 | 快速操作、一鍵觸發 |
Agent 類型與執行位置
flowchart TB
subgraph "Agent 執行位置"
LOCAL[🖥️ Local Agent<br/>在 VS Code 中互動執行]
AUTO[🚀 Autopilot<br/>自主決策完成時機<br/>最多三次迭代]
BG[⚡ Background Agent<br/>後台自主執行<br/>不阻斷開發]
CLOUD[☁️ Cloud Agent<br/>雲端執行<br/>完成後開 PR]
CLI[💻 CLI Agent<br/>終端模式<br/>支援 MCP]
RESEARCH[🔬 Research Agent<br/>深度調研<br/>產出報告]
THIRD[🔌 Third-Party Agent<br/>Anthropic Claude<br/>OpenAI 等]
end
USER[👨💻 開發者] --> LOCAL
USER --> AUTO
USER --> BG
USER --> CLOUD
USER --> CLI
USER --> RESEARCH
USER --> THIRD
LOCAL -->|手動交接| CLOUD
AUTO -->|完成通知| LOCAL
BG -->|完成通知| LOCAL
CLOUD -->|PR Review| LOCAL
RESEARCH -->|Markdown 報告| LOCAL
style LOCAL fill:#3b82f6,color:#fff
style AUTO fill:#10b981,color:#fff
style BG fill:#8b5cf6,color:#fff
style CLOUD fill:#22c55e,color:#fff
style CLI fill:#f59e0b,color:#fff
style RESEARCH fill:#06b6d4,color:#fff
style THIRD fill:#ef4444,color:#fffSession 管理與 Agents Window
VS Code 1.123 起推出 Agents Window(Preview),這是一個專用的 Agent 伴侶視窗,提供跨專案、跨機器的統一 Agent Session 管理體驗:
- 多 Session 並行開啟:可同時開啟多個 Session 視圖,並排檢視比較
- Multiple Chats per Session(1.126 新增):單一 Session 中可開啟多個 Chat 分頁同時進行
- Session Fork:從對話任意節點分岔,探索不同方向而不失去原始上下文
- Session Sync(1.123 新增):Chat Session 自動同步至 GitHub 帳號,跨裝置可搜尋工作歷史
- Chronicle 指令:
/chronicle可搜尋歷史 Session、產生站會報告、取得個人化建議 - Session 級別成本追蹤(1.126 新增):即時查看每個 Session 消耗的 Credits
- 背景發送(1.124 新增):
Alt+Enter將請求發送至背景 Session,不中斷當前操作 - Session 導航(1.124 新增):
Ctrl+R開啟 Session 快速切換器,支援鍵盤導航 - Code Feedback(1.126 新增):在 Agents Window 中對生成程式碼留言,Agent 可透過
listComments/resolveComments回應 - Restore on Reload(1.124 新增):重新載入時自動還原 Session 布局狀態
1.3 VS Code 1.123–1.126 重要新功能摘要
最新穩定版:VS Code 1.126(2026 年 6 月 24 日發布)
VS Code 1.126(2026-06-24)
| 類別 | 功能 | 說明 |
|---|---|---|
| 成本管理 | Session 級別費用追蹤 | 查看完整 Chat Session 消耗的 Credits 與 Token |
| 模型設定 | 統一模型自訂化選擇器 | Context Size 與 Thinking Effort 合併為單一控制介面 |
| 模型設定 | 簡化模型懸停資訊 | 一詞能力描述 + 直達設定的深連結按鈕 |
| Agents Window | Multiple Chats per Session | 單一 Session 可同時開啟多個 Chat,共享工作上下文 |
| Agents Window | Agentic Code Feedback | 對生成程式碼留言,Agent 可透過工具讀取並回應 |
| 安全性 | Restricted Mode 預設開啟 | 新資料夾預設以受限模式開啟,安全瀏覽後再信任 |
VS Code 1.125(2026-06-17)
| 類別 | 功能 | 說明 |
|---|---|---|
| 模型管理 | Install Model Providers | 從 Language Models 編輯器直接安裝第三方模型 Provider |
| 整合瀏覽器 | 網址列搜尋引擎 | 直接在瀏覽器網址列輸入關鍵字進行網頁搜尋 |
| 整合瀏覽器 | Remote Proxy(Preview) | 遠端工作區中瀏覽器流量可透過遠端連線代理 |
| Extension | 自動更新延遲可配置 | extensions.autoUpdateDelay 設定延遲時數(預設 2 小時) |
| 企業 | MDM 原生管理 Copilot 設定 | 透過 Windows / macOS 裝置管理工具統一部署 Copilot 政策 |
VS Code 1.124(2026-06-10)
| 類別 | 功能 | 說明 |
|---|---|---|
| Autopilot | Autopilot 預設啟用 | Agent 自主決策完成時機,不需逐步人工確認 |
| Autopilot | Advanced Autopilot | 小型判定模型評估任務完成度,最多三次迭代 |
| Agents Window | 背景發送 | Alt+Enter 背景啟動 Session,不中斷當前操作 |
| Agents Window | Session 導航 | Ctrl+R 快速切換、Ctrl+Tab 返回上一個 Session |
| Agents Window | Restore on Reload | 重新載入時自動還原所有開啟的 Session 布局 |
| 整合瀏覽器 | 瀏覽歷史 | 自動記錄已造訪頁面,URL 列顯示歷史建議 |
| 整合瀏覽器 | 快速 Agent 文字輸入 | typeInPage 支援 submit 參數,減少工具呼叫次數 |
| 企業 | Copilot Plugin 政策管理 | 集中控制可用 Chat Plugin 與 Plugin Marketplace |
VS Code 1.123(2026-06-03)
| 類別 | 功能 | 說明 |
|---|---|---|
| Agent | Session Sync & Chronicle | Chat Session 自動同步至 GitHub,/chronicle 搜尋歷史 |
| Agent | Research Agent(Preview) | /research 深度研究,產出完整引用的 Markdown 報告 |
| Agent | Agents Window 多 Session | 可多 Session 並排開啟,支援釘選、最大化 |
| 模型 | 1M Context Window | Anthropic 與 OpenAI 相容模型支援 100 萬 Token 上下文 |
| 整合瀏覽器 | 頁面收藏 | 星號收藏常用頁面,URL 列快速存取 |
| 整合瀏覽器 | 區域 / 全頁面截圖 | 截圖後直接加入 Chat 作為上下文 |
| MCP | OAuth Client Credentials | 在 mcp.json 中指定 clientId,支援 Client Secret 安全儲存 |
| MCP | 企業級 MCP 認證(Preview) | ID-JAG 跨應用授權,單次登入 IdP 即取得所有 MCP 資源權杖 |
| Extension | 自動更新延遲機制 | 新版 Extension 發布後預設延遲 2 小時才自動更新 |
| 沙箱 | 網路相關指令重試 | 沙箱中自動重試需要網路的指令(如 git fetch) |
⚠️ 重要變更通知:
- VS Code 1.125 中 Edit Mode 已完全移除,請改用 Agent Mode
- 推理深度由 1.126 的統一模型自訂化選擇器控制(合併 Context Size + Thinking Effort)
- 新資料夾預設以 Restricted Mode 開啟(
security.workspace.trust.startupPrompt預設改為never)- Extension 自動更新設定簡化為
on/off,舊值自動遷移
1.4 VS Code vs IntelliJ 差異分析
| 比較項目 | VS Code | IntelliJ IDEA |
|---|---|---|
| 授權費用 | 免費 | Community 免費 / Ultimate 付費 |
| 啟動速度 | ⚡ 秒級 | 🐢 較慢(大型專案 30s+) |
| 記憶體使用 | 300-500 MB | 2-4 GB |
| Java 支援深度 | ★★★★☆(需 Extension) | ★★★★★(原生) |
| 重構能力 | ★★★☆☆ | ★★★★★ |
| AI 整合 | ★★★★★(Copilot 原生 + Autopilot + Agents Window) | ★★★★☆(需插件) |
| Agent 能力 | ★★★★★(Local/BG/Cloud/CLI/Research Agent) | ★★☆☆☆(有限) |
| Spring Boot 支援 | ★★★★☆ | ★★★★★ |
| 跨語言能力 | ★★★★★ | ★★★☆☆ |
| MCP 支援 | ★★★★★(原生 MCP + OAuth + 企業級認證) | ★★☆☆☆ |
| 學習曲線 | 低 | 中-高 |
| 企業部署 | 簡單(免授權 + MDM 政策管理) | 需管理授權 |
| 整合式瀏覽器 | ★★★★★(搜尋、收藏、遠端 Proxy、截圖) | ★★☆☆☆ |
| 成本追蹤 | ★★★★★(Session 級別 Credits 追蹤) | ★☆☆☆☆ |
| Context Window | ★★★★★(最高 1M Token) | ★★★☆☆ |
🏦 企業實務建議:
- 銀行等大型企業建議以 VS Code + Copilot 為主要開發工具
- VS Code 的 Agent 生態(Local + Cloud + CLI + Research + Autopilot)已遠超 IntelliJ
- Agents Window 的多 Session 管理和 Session Sync 適合團隊協作
- 1.125 的 MDM 原生政策管理讓企業 IT 可透過現有裝置管理工具統一設定
- 搭配 Copilot Chat + Agent Mode 可彌補重構能力的差距
- IntelliJ 可作為特定場景的輔助工具(如複雜 Spring 設定除錯)
2. 開發環境安裝與設定
2.1 必備工具
工具清單
| 工具 | 最低版本 | 建議版本 | 用途 |
|---|---|---|---|
| VS Code | 1.123+ | 1.126+(2026-06-24 發布) | 主要 IDE |
| JDK | 17 | 21(LTS) | Java 執行環境 |
| Maven | 3.8+ | 3.9.9+ | 專案管理 / 依賴管理 |
| Git | 2.40+ | 2.47+ | 版本控管 |
| GitHub Copilot 訂閱 | Free | Business / Enterprise | AI 輔助開發 |
💡 快速安裝:Coding Pack for Java
Windows 與 macOS 使用者可直接下載 Coding Pack for Java,一次安裝 VS Code + JDK + 全部必要 Extension。
🏦 Copilot 訂閱方案說明:
- Copilot Free:每月有限完成與對話次數,適合個人學習評估
- Copilot Pro:個人付費方案,無限補全與對話
- Copilot Business:企業方案,含管理方針、部署管理
- Copilot Enterprise:包含 Knowledge Base、細調、進階安全功能
JDK 安裝(以 Eclipse Temurin 為例)
# Windows - 使用 winget 安裝 JDK 21
winget install EclipseAdoptium.Temurin.21.JDK
# 驗證安裝
java -version
# 預期輸出: openjdk version "21.x.x"
# 設定 JAVA_HOME(系統環境變數)
[System.Environment]::SetEnvironmentVariable("JAVA_HOME", "C:\Program Files\Eclipse Adoptium\jdk-21.0.x-hotspot", "Machine")Maven 安裝
# Windows - 使用 winget
winget install Apache.Maven
# 或手動下載後設定環境變數
# 1. 從 https://maven.apache.org/download.cgi 下載
# 2. 解壓至 C:\tools\apache-maven-3.9.9
# 3. 設定環境變數
[System.Environment]::SetEnvironmentVariable("MAVEN_HOME", "C:\tools\apache-maven-3.9.9", "Machine")
$path = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
[System.Environment]::SetEnvironmentVariable("Path", "$path;C:\tools\apache-maven-3.9.9\bin", "Machine")
# 驗證
mvn -version2.2 VS Code Extension 推薦
必裝 Extension
| Extension | ID | 用途 |
|---|---|---|
| Extension Pack for Java | vscjava.vscode-java-pack | 一次安裝 6 個核心擴充套件(詳見下表) |
| Spring Boot Extension Pack | vmware.vscode-boot-dev-pack | Spring Boot Dashboard、Initializr、屬性提示 |
| GitHub Copilot | github.copilot | AI 程式碼補全、Inline Completion |
| GitHub Copilot Chat | github.copilot-chat | Chat、Agent Mode、CLI Agent |
| REST Client | humao.rest-client | HTTP API 測試(取代 Postman) |
Extension Pack for Java 包含內容(官方 6 個擴充)
| 擴充套件 | ID | 功能 |
|---|---|---|
| Language Support for Java™ | redhat.java | 語法分析、IntelliSense、重構、Quick Fix |
| Debugger for Java | vscjava.vscode-java-debug | 中斷點、變數監控、Hot Code Replace |
| Test Runner for Java | vscjava.vscode-java-test | JUnit 5、TestNG 視覺化執行與報告 |
| Maven for Java | vscjava.vscode-maven | Maven 生命週期、依賴樹、快速操作 |
| Project Manager for Java | vscjava.vscode-java-dependency | 專案結構瀏覽、依賴管理 |
| IntelliCode | visualstudiointellicode.vscodeintellicode | AI 智慧補全(依據最佳實務排序建議) |
建議安裝 Extension
| Extension | ID | 用途 |
|---|---|---|
| GitLens | eamodio.gitlens | Git 進階視覺化、Blame、歷史 |
| SonarQube for IDE | sonarsource.sonarlint-vscode | 即時程式碼品質掃描 |
| Thunder Client | rangav.vscode-thunder-client | 輕量 API 測試(帶 GUI) |
| XML | redhat.vscode-xml | XML / POM 支援 |
| YAML | redhat.vscode-yaml | YAML 設定檔支援 |
| Docker | ms-azuretools.vscode-docker | Docker 管理 |
| Error Lens | usernamehw.errorlens | 行內顯示錯誤訊息 |
一鍵安裝指令
# 在終端執行批次安裝
code --install-extension vscjava.vscode-java-pack
code --install-extension vmware.vscode-boot-dev-pack
code --install-extension github.copilot
code --install-extension github.copilot-chat
code --install-extension humao.rest-client
code --install-extension eamodio.gitlens
code --install-extension sonarsource.sonarlint-vscode
code --install-extension redhat.vscode-xml
code --install-extension redhat.vscode-yaml
code --install-extension usernamehw.errorlens2.3 環境設定步驟
步驟 1:VS Code 設定(settings.json)
按 Ctrl + Shift + P → 輸入 Preferences: Open User Settings (JSON),加入以下設定:
{
// Java 設定
"java.configuration.runtimes": [
{
"name": "JavaSE-21",
"path": "C:\\Program Files\\Eclipse Adoptium\\jdk-21.0.5-hotspot",
"default": true
}
],
"java.jdt.ls.java.home": "C:\\Program Files\\Eclipse Adoptium\\jdk-21.0.5-hotspot",
"java.compile.nullAnalysis.mode": "automatic",
// Maven 設定
"java.configuration.maven.userSettings": "C:\\Users\\<USER>\\.m2\\settings.xml",
"maven.executable.path": "C:\\tools\\apache-maven-3.9.9\\bin\\mvn.cmd",
"maven.terminal.useJavaHome": true,
// 編輯器設定
"editor.fontSize": 14,
"editor.tabSize": 4,
"editor.formatOnSave": true,
"editor.minimap.enabled": false,
"files.encoding": "utf8",
"files.autoSave": "afterDelay",
// Copilot 設定
"github.copilot.enable": {
"*": true,
"yaml": true,
"markdown": true,
"json": true
},
"github.copilot.chat.localeOverride": "zh-TW",
// 終端設定
"terminal.integrated.defaultProfile.windows": "PowerShell",
"terminal.integrated.fontSize": 13
}步驟 2:Workspace 設定
建立 .vscode/settings.json(專案層級):
{
"java.configuration.updateBuildConfiguration": "automatic",
"java.format.settings.url": ".vscode/java-formatter.xml",
"java.saveActions.organizeImports": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
},
"files.exclude": {
"**/target": true,
"**/.classpath": true,
"**/.project": true,
"**/.settings": true,
"**/.factorypath": true
}
}步驟 3:登入 GitHub 並啟用 Copilot
- 點擊 VS Code 左下角 帳戶圖示 → 登入 GitHub
- 瀏覽器自動開啟,完成 OAuth 授權
- 回到 VS Code,確認 Copilot 圖示出現在狀態列
- 開啟任意
.java檔案,看到 Copilot 建議即表示成功
flowchart TD
A[開啟 VS Code] --> B[點擊帳戶圖示]
B --> C[選擇 Sign in with GitHub]
C --> D[瀏覽器 OAuth 授權]
D --> E[回到 VS Code]
E --> F{狀態列顯示 Copilot 圖示?}
F -->|是| G[✅ 設定完成]
F -->|否| H[檢查訂閱狀態]
H --> I[GitHub Settings > Copilot]
I --> F步驟 4:驗證環境
# 驗證所有工具
java -version # Java 21+
mvn -version # Maven 3.9+
git --version # Git 2.40+
code --version # VS Code 1.126+🏦 企業實務注意:
- 銀行環境通常有 Proxy,需在
settings.json設定"http.proxy": "http://proxy.bank.com:8080"- Maven 需在
settings.xml中設定 Proxy 與企業 Nexus Repository- Copilot 需確認
github.com與copilot-proxy.githubusercontent.com已列入白名單- 若使用 Cloud Agent,需額外開放
api.github.com呼叫
2.4 Copilot 自訂化設定
Chat Customizations Editor(VS Code 1.113+ / 1.125 GA)
- 開啟:
Ctrl + Shift + P→Chat: Open Chat Customizations - 在統一 UI 中管理:
- Custom Instructions:自訂指令檔(
.github/copilot-instructions.md) - Prompt Files:可複用的 Prompt 範本(
.github/prompts/*.prompt.md) - Custom Agents:自定義 Agent(
.github/agents/*.agent.md) - Agent Skills:自定義技能(
.github/skills/*.skill.md)
- Custom Instructions:自訂指令檔(
專案級 Copilot 設定(/init 指令)
在 Chat 中輸入 /init,Copilot 會協助建立專案 AI 設定檔:
/init自動生成的檔案結構:
.github/
├── copilot-instructions.md ← 全域自訂指令
├── prompts/
│ ├── code-review.prompt.md ← Code Review Prompt
│ └── test-gen.prompt.md ← 測試生成 Prompt
├── agents/
│ └── spring-expert.agent.md ← Spring Boot 專家 Agent
└── skills/
└── db-migration.skill.md ← DB Migration 技能範例:copilot-instructions.md
# 專案開發指引
- 使用 Java 21、Spring Boot 3.4.x
- 遵循 Clean Architecture 分層
- 所有 API 端點需有 @Valid 驗證
- Service 層需包含 @Transactional
- 例外處理使用 @RestControllerAdvice
- 繁體中文註解
- 单元測試使用 JUnit 5 + Mockito範例:自定義 Agent(.github/agents/spring-expert.agent.md)
---
name: spring-expert
description: "Spring Boot 專家 Agent,負責架構設計與 Code Review"
tools:
- semantic_search
- run_in_terminal
- read_file
---
你是一位資深 Spring Boot 專家。請在回答時:
1. 始終引用 Spring 官方最佳實務
2. 程式碼遵循本專案 copilot-instructions.md
3. 提供 OWASP Top 10 安全建議3. 建立第一個 Spring Boot 專案
3.1 使用 Spring Initializr
方法一:VS Code 內建(推薦)
- 按
Ctrl + Shift + P - 輸入
Spring Initializr: Create a Maven Project - 依序選擇:
- Spring Boot 版本:3.4.x(最新穩定版)
- 語言:Java
- Group Id:
com.bank.demo - Artifact Id:
banking-api - 打包方式:Jar
- Java 版本:21
- 依賴項:
- Spring Web
- Spring Data JPA
- Spring Security
- Spring Boot Actuator
- Lombok
- H2 Database(開發用)
- Validation
方法二:使用 Copilot Chat 生成
在 Copilot Chat 中輸入:
@workspace /new 建立一個 Spring Boot 3.4 Maven 專案,Group ID 為 com.bank.demo,
包含 Web、JPA、Security、Actuator、Lombok、H2、Validation 依賴生成的 pom.xml 核心內容
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.1</version>
<relativePath/>
</parent>
<groupId>com.bank.demo</groupId>
<artifactId>banking-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>banking-api</name>
<description>Banking API Demo Project</description>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 測試依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>3.2 專案結構說明
banking-api/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/bank/demo/
│ │ │ ├── BankingApiApplication.java ← 啟動類
│ │ │ ├── controller/ ← REST API 端點
│ │ │ │ └── AccountController.java
│ │ │ ├── service/ ← 業務邏輯層
│ │ │ │ ├── AccountService.java ← 介面
│ │ │ │ └── impl/
│ │ │ │ └── AccountServiceImpl.java ← 實作
│ │ │ ├── repository/ ← 資料存取層
│ │ │ │ └── AccountRepository.java
│ │ │ ├── entity/ ← JPA Entity
│ │ │ │ └── Account.java
│ │ │ ├── dto/ ← 資料傳輸物件
│ │ │ │ ├── request/
│ │ │ │ │ └── CreateAccountRequest.java
│ │ │ │ └── response/
│ │ │ │ └── AccountResponse.java
│ │ │ ├── config/ ← 設定類
│ │ │ │ └── SecurityConfig.java
│ │ │ └── exception/ ← 全域例外處理
│ │ │ ├── GlobalExceptionHandler.java
│ │ │ └── BusinessException.java
│ │ └── resources/
│ │ ├── application.yml ← 主設定檔
│ │ ├── application-dev.yml ← 開發環境
│ │ └── application-prod.yml ← 正式環境
│ └── test/
│ └── java/
│ └── com/bank/demo/
│ ├── controller/
│ │ └── AccountControllerTest.java
│ └── service/
│ └── AccountServiceTest.java
├── .vscode/
│ ├── settings.json
│ ├── launch.json
│ └── tasks.json
├── .github/
│ ├── copilot-instructions.md ← Copilot 專案指引
│ └── workflows/
│ └── ci.yml
├── pom.xml
└── README.md各層職責說明
graph TB
Client[🌐 客戶端] --> Controller
subgraph "Spring Boot 應用程式"
Controller[🎯 Controller<br/>接收 HTTP 請求<br/>參數驗證] --> Service
Service[⚙️ Service<br/>業務邏輯<br/>交易管理] --> Repository
Repository[💾 Repository<br/>資料存取<br/>SQL 查詢] --> DB[(🗄️ Database)]
end
Controller -.->|使用| DTO[📦 DTO<br/>Request / Response]
Service -.->|操作| Entity[📋 Entity<br/>JPA 映射]
style Controller fill:#3b82f6,color:#fff
style Service fill:#22c55e,color:#fff
style Repository fill:#f59e0b,color:#fff3.3 執行與測試 API
application.yml
server:
port: 8080
spring:
application:
name: banking-api
datasource:
url: jdbc:h2:mem:bankingdb
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
h2:
console:
enabled: true
path: /h2-console
# Actuator
management:
endpoints:
web:
exposure:
include: health,info,metrics建立第一個 Controller
package com.bank.demo.controller;
import com.bank.demo.dto.response.AccountResponse;
import com.bank.demo.service.AccountService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 帳戶管理 REST API
*/
@RestController
@RequestMapping("/api/v1/accounts")
@RequiredArgsConstructor
public class AccountController {
private final AccountService accountService;
/**
* 查詢所有帳戶
* @return 帳戶清單
*/
@GetMapping
public ResponseEntity<List<AccountResponse>> getAllAccounts() {
return ResponseEntity.ok(accountService.findAll());
}
/**
* 依帳號查詢
* @param accountNumber 帳號
* @return 帳戶資訊
*/
@GetMapping("/{accountNumber}")
public ResponseEntity<AccountResponse> getAccount(
@PathVariable String accountNumber) {
return ResponseEntity.ok(accountService.findByAccountNumber(accountNumber));
}
}啟動與測試
# 方法 1:VS Code 終端
mvn spring-boot:run
# 方法 2:VS Code Spring Boot Dashboard
# 點擊左側 Spring Boot 圖示 → 按下 ▶️ 啟動使用 REST Client 測試
在專案根目錄建立 api-test.http:
### 健康檢查
GET http://localhost:8080/actuator/health
Content-Type: application/json
### 查詢所有帳戶
GET http://localhost:8080/api/v1/accounts
Content-Type: application/json
### 建立帳戶
POST http://localhost:8080/api/v1/accounts
Content-Type: application/json
{
"accountNumber": "0001-2345-6789",
"accountName": "王小明",
"balance": 100000.00,
"accountType": "SAVINGS"
}💡 提示:安裝 REST Client Extension 後,
.http文件中每個請求上方會出現Send Request按鈕,點擊即可執行。
3.4 使用整合式瀏覽器測試 Web 應用
VS Code 的整合式瀏覽器(Integrated Browser)持續強化,已從簡易瀏覽器進化為功能完整的開發用瀏覽器,非常適合測試 Spring Boot Web 應用。
開啟整合式瀏覽器
Ctrl + Shift + P → Simple Browser: Show
輸入 URL:http://localhost:8080功能亮點
| 功能 | 說明 | 快捷鍵 |
|---|---|---|
| 自簽憑證支援 | 開發 HTTPS 應用時自動信任自簽憑證 | 自動 |
| Quick Open Tab | 快速搜尋已開啟的瀏覽器分頁 | Ctrl+Shift+A |
| 頁面收藏 | 星號收藏常用頁面 | URL 列星號 |
| 瀏覽歷史 | 自動記錄已造訪頁面 | Ctrl+H |
| 網頁搜尋 | URL 列直接輸入關鍵字搜尋 | 自動 |
| 區域截圖 | 選取區域截圖加入 Chat | 右鍵選單 |
| 與 Agent 整合 | Agent 可自動開啟瀏覽器、輸入文字、驗證結果 | 自動 |
| Remote Proxy | 遠端工作區安全代理 HTTP 流量 | 設定啟用 |
實務應用場景
flowchart LR
A[啟動 Spring Boot] --> B[整合式瀏覽器<br/>localhost:8080]
B --> C[H2 Console<br/>/h2-console]
B --> D[Swagger UI<br/>/swagger-ui.html]
B --> E[Actuator<br/>/actuator/health]
style B fill:#3b82f6,color:#fff💡 優勢:不需切換到外部瀏覽器,所有開發、測試、除錯都在 VS Code 內完成。Agent Mode 可直接操作整合式瀏覽器驗證變更結果。瀏覽器還支援收藏常用頁面、瀏覽歷史、網頁搜尋和截圖加入 Chat 等功能。
4. GitHub Copilot 實戰應用
4.1 基本用法 — Inline Completion 與 Inline Chat
自動補全(Inline Completion)
在 Java 檔案中,Copilot 會根據上下文自動提供建議。
範例:輸入方法簽名,Copilot 自動補全實作
// 只需輸入方法簽名和註解,Copilot 會自動產生完整實作
/**
* 計算帳戶利息
* @param balance 帳戶餘額
* @param annualRate 年利率
* @param days 天數
* @return 利息金額
*/
public BigDecimal calculateInterest(BigDecimal balance, BigDecimal annualRate, int days) {
// Copilot 自動補全 ↓
return balance
.multiply(annualRate)
.multiply(BigDecimal.valueOf(days))
.divide(BigDecimal.valueOf(365), 2, RoundingMode.HALF_UP);
}操作快捷鍵:
| 快捷鍵 | 功能 |
|---|---|
Tab | 接受建議 |
Esc | 拒絕建議 |
Alt + ] | 下一個建議 |
Alt + [ | 上一個建議 |
Ctrl + Enter | 開啟建議面板(查看多個建議) |
Next Edit Suggestions(NES)
除了當前游標位置的補全,Copilot 還會預測你下一步可能要編輯的位置,並提供建議。通過 Tab 即可跳轉並接受。
適用場景:
- 新增了一個 Entity 欄位,自動建議更新對應的 Builder、DTO、Repository 查詢
- 修改了方法簽名,自動建議更新呼叫處
Inline Chat(行內對話)
按 Ctrl + I 在編輯器行內開啟對話,不離開編輯器即可操作:
# 選取一段程式碼後按 Ctrl+I
重構這段程式,使用 Stream API 替代 for 迴圈
加入 null check 並拋出 BusinessException
將這個方法拆分為兩個更小的方法註解驅動生成程式碼
技巧:先寫註解,再讓 Copilot 產生程式碼。
// 建立一個帳戶交易紀錄的 Entity,包含交易編號、帳號、交易類型(存款/提款/轉帳)、
// 金額、交易時間、備註,使用 JPA 註解映射到 transaction_records 表
// Copilot 將自動產生以下 Entity ↓
@Entity
@Table(name = "transaction_records")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class TransactionRecord {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "transaction_no", unique = true, nullable = false, length = 32)
private String transactionNo;
@Column(name = "account_number", nullable = false, length = 20)
private String accountNumber;
@Enumerated(EnumType.STRING)
@Column(name = "transaction_type", nullable = false)
private TransactionType transactionType;
@Column(name = "amount", nullable = false, precision = 18, scale = 2)
private BigDecimal amount;
@Column(name = "transaction_time", nullable = false)
private LocalDateTime transactionTime;
@Column(name = "remark", length = 500)
private String remark;
public enum TransactionType {
DEPOSIT, // 存款
WITHDRAWAL, // 提款
TRANSFER // 轉帳
}
}4.2 Copilot Chat 進階用法
Copilot Chat 使用方式
開啟方式
- 快捷鍵:
Ctrl + Shift + I(開啟 Chat 面板) - 快速提問:
Ctrl + I(行內 Inline Chat) - 側邊欄:點擊左側 Copilot 圖示
Chat 指令與上下文提供者(Context Providers)
| 指令 / 符號 | 類型 | 功能 | 範例 |
|---|---|---|---|
@workspace | Context | 搜尋整個工作區 | @workspace 找到所有 Controller 類別 |
/explain | 指令 | 解釋程式碼 | 選取程式碼後 /explain |
/fix | 指令 | 修復問題 | 選取有問題的程式碼後 /fix |
/tests | 指令 | 產生測試 | 選取類別後 /tests |
/doc | 指令 | 產生文件 | 選取方法後 /doc |
/new | 指令 | 建立新專案/檔案 | /new Spring Boot REST controller for orders |
/init | 指令 | 建立專案 AI 設定檔 | /init(生成 copilot-instructions.md 等) |
#file | Context | 引用特定檔案 | 看一下 #file:pom.xml 有哪些依賴 |
#selection | Context | 引用目前選取 | 解釋 #selection 的用途 |
#codebase | Context | 搜尋整個程式碼庫 | #codebase 查找所有使用 @Transactional 的檔案 |
#terminal | Context | 引用終端輸出 | 解釋 #terminal 的錯誤訊息 |
#problems | Context | 引用問題面板 | 修復 #problems 中的所有錯誤 |
使用 Copilot 生成 Service 層
在 Copilot Chat 中輸入:
@workspace 根據 AccountController 和 Account Entity,
幫我生成完整的 AccountService 介面和 AccountServiceImpl 實作類別,
包含:
1. CRUD 操作
2. 轉帳功能(含交易紀錄)
3. 使用 @Transactional 管理交易
4. 適當的例外處理
5. 使用 Slf4j 記錄日誌Copilot 生成結果:
package com.bank.demo.service;
import com.bank.demo.dto.request.CreateAccountRequest;
import com.bank.demo.dto.request.TransferRequest;
import com.bank.demo.dto.response.AccountResponse;
import java.util.List;
/**
* 帳戶服務介面
*/
public interface AccountService {
AccountResponse create(CreateAccountRequest request);
AccountResponse findByAccountNumber(String accountNumber);
List<AccountResponse> findAll();
AccountResponse update(String accountNumber, CreateAccountRequest request);
void delete(String accountNumber);
void transfer(TransferRequest request);
}package com.bank.demo.service.impl;
import com.bank.demo.dto.request.CreateAccountRequest;
import com.bank.demo.dto.request.TransferRequest;
import com.bank.demo.dto.response.AccountResponse;
import com.bank.demo.entity.Account;
import com.bank.demo.exception.BusinessException;
import com.bank.demo.repository.AccountRepository;
import com.bank.demo.service.AccountService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;
/**
* 帳戶服務實作
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class AccountServiceImpl implements AccountService {
private final AccountRepository accountRepository;
@Override
@Transactional
public AccountResponse create(CreateAccountRequest request) {
log.info("建立帳戶: {}", request.getAccountNumber());
if (accountRepository.existsByAccountNumber(request.getAccountNumber())) {
throw new BusinessException("帳號已存在: " + request.getAccountNumber());
}
Account account = Account.builder()
.accountNumber(request.getAccountNumber())
.accountName(request.getAccountName())
.balance(request.getBalance())
.accountType(request.getAccountType())
.build();
Account saved = accountRepository.save(account);
log.info("帳戶建立成功: {}", saved.getAccountNumber());
return toResponse(saved);
}
@Override
@Transactional(readOnly = true)
public AccountResponse findByAccountNumber(String accountNumber) {
return accountRepository.findByAccountNumber(accountNumber)
.map(this::toResponse)
.orElseThrow(() -> new BusinessException("帳戶不存在: " + accountNumber));
}
@Override
@Transactional(readOnly = true)
public List<AccountResponse> findAll() {
return accountRepository.findAll().stream()
.map(this::toResponse)
.collect(Collectors.toList());
}
@Override
@Transactional
public void transfer(TransferRequest request) {
log.info("轉帳: {} -> {}, 金額: {}",
request.getFromAccount(), request.getToAccount(), request.getAmount());
Account from = accountRepository.findByAccountNumber(request.getFromAccount())
.orElseThrow(() -> new BusinessException("轉出帳戶不存在"));
Account to = accountRepository.findByAccountNumber(request.getToAccount())
.orElseThrow(() -> new BusinessException("轉入帳戶不存在"));
if (from.getBalance().compareTo(request.getAmount()) < 0) {
throw new BusinessException("餘額不足,目前餘額: " + from.getBalance());
}
from.setBalance(from.getBalance().subtract(request.getAmount()));
to.setBalance(to.getBalance().add(request.getAmount()));
accountRepository.save(from);
accountRepository.save(to);
log.info("轉帳成功: {} -> {}, 金額: {}",
request.getFromAccount(), request.getToAccount(), request.getAmount());
}
@Override
@Transactional
public AccountResponse update(String accountNumber, CreateAccountRequest request) {
Account account = accountRepository.findByAccountNumber(accountNumber)
.orElseThrow(() -> new BusinessException("帳戶不存在: " + accountNumber));
account.setAccountName(request.getAccountName());
account.setAccountType(request.getAccountType());
return toResponse(accountRepository.save(account));
}
@Override
@Transactional
public void delete(String accountNumber) {
Account account = accountRepository.findByAccountNumber(accountNumber)
.orElseThrow(() -> new BusinessException("帳戶不存在: " + accountNumber));
accountRepository.delete(account);
log.info("帳戶已刪除: {}", accountNumber);
}
private AccountResponse toResponse(Account account) {
return AccountResponse.builder()
.accountNumber(account.getAccountNumber())
.accountName(account.getAccountName())
.balance(account.getBalance())
.accountType(account.getAccountType())
.build();
}
}使用 Copilot 生成測試程式
選取 AccountServiceImpl 類別,在 Chat 中輸入:
/tests 請為 AccountServiceImpl 生成完整的 JUnit 5 測試,
使用 Mockito mock AccountRepository,
需涵蓋:正常路徑、帳戶不存在、餘額不足等測試案例package com.bank.demo.service;
import com.bank.demo.dto.request.CreateAccountRequest;
import com.bank.demo.dto.request.TransferRequest;
import com.bank.demo.dto.response.AccountResponse;
import com.bank.demo.entity.Account;
import com.bank.demo.exception.BusinessException;
import com.bank.demo.repository.AccountRepository;
import com.bank.demo.service.impl.AccountServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.math.BigDecimal;
import java.util.Optional;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
@DisplayName("帳戶服務測試")
class AccountServiceImplTest {
@Mock
private AccountRepository accountRepository;
@InjectMocks
private AccountServiceImpl accountService;
private Account testAccount;
@BeforeEach
void setUp() {
testAccount = Account.builder()
.id(1L)
.accountNumber("0001-2345-6789")
.accountName("王小明")
.balance(new BigDecimal("100000.00"))
.accountType("SAVINGS")
.build();
}
@Nested
@DisplayName("查詢帳戶")
class FindAccountTests {
@Test
@DisplayName("帳號存在時應回傳帳戶資訊")
void shouldReturnAccountWhenExists() {
when(accountRepository.findByAccountNumber("0001-2345-6789"))
.thenReturn(Optional.of(testAccount));
AccountResponse response = accountService
.findByAccountNumber("0001-2345-6789");
assertThat(response.getAccountNumber()).isEqualTo("0001-2345-6789");
assertThat(response.getAccountName()).isEqualTo("王小明");
}
@Test
@DisplayName("帳號不存在時應拋出 BusinessException")
void shouldThrowWhenAccountNotFound() {
when(accountRepository.findByAccountNumber("9999-9999-9999"))
.thenReturn(Optional.empty());
assertThatThrownBy(() ->
accountService.findByAccountNumber("9999-9999-9999"))
.isInstanceOf(BusinessException.class)
.hasMessageContaining("帳戶不存在");
}
}
@Nested
@DisplayName("轉帳功能")
class TransferTests {
@Test
@DisplayName("餘額充足時轉帳應成功")
void shouldTransferSuccessfully() {
Account toAccount = Account.builder()
.id(2L)
.accountNumber("0002-3456-7890")
.accountName("李大華")
.balance(new BigDecimal("50000.00"))
.build();
when(accountRepository.findByAccountNumber("0001-2345-6789"))
.thenReturn(Optional.of(testAccount));
when(accountRepository.findByAccountNumber("0002-3456-7890"))
.thenReturn(Optional.of(toAccount));
TransferRequest request = TransferRequest.builder()
.fromAccount("0001-2345-6789")
.toAccount("0002-3456-7890")
.amount(new BigDecimal("30000.00"))
.build();
accountService.transfer(request);
assertThat(testAccount.getBalance())
.isEqualByComparingTo("70000.00");
assertThat(toAccount.getBalance())
.isEqualByComparingTo("80000.00");
verify(accountRepository, times(2)).save(any(Account.class));
}
@Test
@DisplayName("餘額不足時應拋出例外")
void shouldThrowWhenInsufficientBalance() {
Account toAccount = Account.builder()
.accountNumber("0002-3456-7890")
.balance(BigDecimal.ZERO)
.build();
when(accountRepository.findByAccountNumber("0001-2345-6789"))
.thenReturn(Optional.of(testAccount));
when(accountRepository.findByAccountNumber("0002-3456-7890"))
.thenReturn(Optional.of(toAccount));
TransferRequest request = TransferRequest.builder()
.fromAccount("0001-2345-6789")
.toAccount("0002-3456-7890")
.amount(new BigDecimal("200000.00"))
.build();
assertThatThrownBy(() -> accountService.transfer(request))
.isInstanceOf(BusinessException.class)
.hasMessageContaining("餘額不足");
}
}
}4.3 Agent Mode 與 Autopilot 深度指南
Agent Mode 是什麼?
Agent Mode 是 Copilot 最強大的模式,能自主完成多步驟任務:編輯檔案、執行終端指令、讀取輸出並自我修正。與傳統 Chat 不同,Agent 不只回答問題,更會主動行動。
Autopilot(VS Code 1.124 Preview)
Autopilot 是 Chat 的權限層級之一,讓 Agent 可自主決定何時繼續迭代、何時停止,無需逐步人工確認每個動作。
核心機制:
- 一個小型判定模型讀取對話紀錄,決定任務是否完成
- 任務目標以 Tooltip 顯示在 Chat 上方,使用者可隨時檢視
- 最多迭代三次後自動停止,防止無限迴圈
- 組織可透過
chat.tools.global.autoApprove政策設定控制
開啟方式:
方法 1:Chat 面板 → 輸入框底部切換 Permission Level 為 Autopilot
方法 2:設定 chat.permissions.default 為 autopilot
方法 3:設定 chat.autopilot.advanced.enabled = true 啟用進階判定開啟 Agent Mode
方法 1:Chat 面板 → 切換至 Agent Mode(下拉選單)
方法 2:Ctrl + Shift + I → 選擇 Agent Mode
方法 3:使用 Plan Agent 先規劃再執行Agent Mode 工作流程
flowchart TD
A[👨💻 使用者需求] --> B[🧠 Agent 分析需求]
B --> C{是否需要規劃?}
C -->|簡單任務| D[直接執行]
C -->|複雜任務| E[Plan Agent<br/>建立實作計畫]
E --> F[使用者確認計畫]
F --> G[Implementation Agent]
D --> G
G --> H[編輯檔案]
G --> I[執行終端指令]
G --> J[讀取輸出]
H --> K{編譯/測試通過?}
I --> K
J --> K
K -->|失敗| L[自我修正]
L --> G
K -->|成功| M{Autopilot 判定完成?}
M -->|否,繼續迭代| G
M -->|是| N[✅ 完成]
style B fill:#3b82f6,color:#fff
style E fill:#8b5cf6,color:#fff
style G fill:#22c55e,color:#fff
style L fill:#ef4444,color:#fff
style M fill:#10b981,color:#fffPermission Levels(權限層級)
| 層級 | 說明 | Agent 自主程度 |
|---|---|---|
| Ask | 每個動作都需確認 | 最低 — 逐步確認 |
| Auto-approve | 預設允許大部分動作 | 中等 — 自動允許安全操作 |
| Autopilot | Agent 自主判斷完成時機 | 最高 — 自主迭代最多三次 |
Local Agent 實戰範例
範例 1:建立完整模組
在 Agent Mode 中輸入:
建立完整的「交易紀錄模組」(TransactionRecord),包含:
1. Entity(映射 transaction_records 表)
2. Repository(含自訂查詢:按帳號查、按日期範圍查)
3. Service + ServiceImpl(含交易紀錄新增、查詢)
4. Controller(REST API,路徑 /api/v1/transactions)
5. DTO(Request + Response)
6. JUnit 測試
放在 com.bank.demo.transaction 套件下,
遵循 #file:.github/copilot-instructions.md 的規範Agent 會依序:
- 建立所有 Java 檔案
- 更新必要的 import
- 執行
mvn compile確認編譯通過 - 執行
mvn test確認測試通過 - 自動修正編譯錯誤後重試
範例 2:重構既有程式碼
將 AccountServiceImpl 中的轉帳邏輯抽取為獨立的 TransferService,
並確保:
1. 原有測試仍然通過
2. 新增 TransferService 的單元測試
3. Controller 改為注入 TransferService
4. 使用 @Transactional 管理事務Background Agent
Background Agent 在背景自主執行任務,不佔用你的 VS Code 操作介面:
flowchart LR
A[指派任務] --> B[Background Agent<br/>背景執行]
B --> C[開發者繼續<br/>其他工作]
B --> D[完成通知]
D --> E[檢視結果]
style B fill:#8b5cf6,color:#fff適用場景:
- 長時間執行的重構任務
- 大量測試生成
- 文件自動生成
- 依賴更新與相容性檢查
Cloud Agent(Preview)
Cloud Agent 在 GitHub 雲端執行,完成後自動開 PR:
- 在 GitHub 上建立 Issue 或在 VS Code 中指派
- Cloud Agent 建立分支、編碼、測試
- 自動開 Pull Request
- 開發者在 VS Code 中 Review
適用場景:
- 跨時區團隊協作
- 大規模重構(不影響本地環境)
- 自動化 Issue 處理
Plan Agent
Plan Agent 專門負責將任務拆解為結構化計畫,再交給 Implementation Agent 執行:
在 Chat 中使用 Plan Agent:
@plan 我想將目前的單體架構拆分為以下微服務:
1. user-service(使用者管理)
2. account-service(帳戶管理)
3. transaction-service(交易管理)
請列出詳細的實作步驟、需要修改的檔案清單、以及可能的風險Plan Agent 會生成:
- 結構化步驟清單
- 預估影響範圍
- 風險評估
- 可由 Implementation Agent 逐步執行
Nested Subagents
Subagent 可以呼叫其他 Subagent,形成多層級工作流:
主 Agent → 呼叫 Spring Expert Subagent(分析架構)
→ 呼叫 Test Subagent(生成測試)
→ 呼叫 Security Subagent(安全審查)Sandbox 網路指令重試(1.123)
當 Local Agent 在沙箱中執行需要網路的指令(如 git fetch、mvn dependency:resolve),若指令因網路權限被阻擋,VS Code 會自動以允許網路的方式重試,同時保持檔案系統隔離保護。
🏦 企業最佳實務:
- Agent Mode 操作的每一步都需要使用者確認(可設定 Permission Level)
- 建議使用 Plan Agent 先規劃再執行,降低風險
- Autopilot 模式適合信任度高的任務(如產生 Boilerplate、跑測試)
- 機敏環境建議限制為 Local Agent + Ask 權限,避免程式碼離開工作站
- Background / Cloud Agent 產出的程式碼必須經過 Code Review
- 使用
chat.permissions.default設定團隊預設權限層級
4.4 Agents Window 與 Session 管理
Agents Window 概述
Agents Window 是 VS Code 1.123 起推出的專用伴侶視窗,為探索、迭代與檢視 Agent Session 而優化。它獨立於主編輯器視窗運行,可跨專案、跨機器管理所有 Agent 工作。
開啟 Agents Window
Ctrl + Shift + P → Agents: Open Agents Window
或從 Activity Bar 的 Agents 圖示開啟核心功能
flowchart TB
subgraph "Agents Window 核心功能"
MULTI[📑 多 Session 並排<br/>Pin / Maximize / Side-by-Side]
NAV[🧭 Session 導航<br/>Ctrl+R 快速切換<br/>Ctrl+Tab 歷史跳轉]
CHAT[💬 Multiple Chats<br/>單一 Session 多 Chat 分頁<br/>共享工作上下文]
SYNC[🔄 Session Sync<br/>自動同步至 GitHub<br/>跨裝置可搜尋]
COST[💰 成本追蹤<br/>Session 級別 Credits<br/>Token 用量統計]
FEED[📝 Code Feedback<br/>對程式碼留言<br/>Agent 自動回應]
end
style MULTI fill:#3b82f6,color:#fff
style NAV fill:#22c55e,color:#fff
style CHAT fill:#f59e0b,color:#fff
style SYNC fill:#8b5cf6,color:#fff
style COST fill:#ef4444,color:#fff
style FEED fill:#06b6d4,color:#fff多 Session 並排操作
| 操作 | 方式 |
|---|---|
| 開啟至側邊 | Sessions 清單右鍵 → Open to the Side |
| 拖曳開啟 | 從清單拖放至 Session 區域 |
| Alt + 點擊 | 按住 Alt 點擊 Session |
| 釘選 Session | Session 視圖右上角 Pin 按鈕(不被新 Session 取代) |
| 最大化 | Session 視圖右上角 Maximize(佔滿所有 Session 區域) |
| 關閉所有 | Ctrl+K Ctrl+W 一鍵關閉所有 Session |
Session Sync 與 Chronicle
啟用 Session Sync 後(chat.sessionSync.enabled),所有 Chat Session 自動同步至 GitHub 帳號:
# 在 Chat 中使用 Chronicle 指令
/chronicle search 上週我做了哪些 Spring Boot 相關的修改?
/chronicle standup 產生今日站會報告
/chronicle tips 根據我的開發習慣給出建議Chronicle 可查詢的資訊:
- 對話內容與 AI 回應
- 觸及的檔案清單
- Repository 上下文(分支、時間戳)
- 相關的 PR、Issue、Commit 引用
Multiple Chats per Session(1.126)
單一 Session 可同時開啟多個 Chat 分頁:
情境:主 Chat 正在讓 Agent 實作功能
→ 點擊 Session 工具列的 "+" 開啟第二個 Chat
→ 第二個 Chat 用來 Review 程式碼或撰寫測試
→ 兩個 Chat 共享相同 Session 和工作上下文
→ 各 Chat 保有獨立對話歷史Agentic Code Feedback(1.126)
在 Agents Window 中,可直接對 Agent 產生的程式碼留下評論:
- Agent 透過
listComments和resolveComments工具回應 - 即使斷開連線,評論仍儲存在 Agent Host 上
- 支援 PR Review 評論:接受後提交給 Agent 處理
- 可使用
/code-review技能自動產生行內評論
成本追蹤(1.126)
Session 資訊彈出視窗顯示:
- 整個 Session 消耗的 Credits 總計
- Context Window Token 使用量
- 可識別高成本 Session,及時調整使用模式
🏦 企業最佳實務:
- 啟用 Session Sync 讓團隊可追溯 AI 協作歷史
- 利用
/chronicle standup自動產生開發日報- 監控 Session 成本,避免團隊成員超額使用
- 善用 Multiple Chats 同時進行開發與 Review
- 利用 Code Feedback 在 Agent 生成程式碼上直接標註修改意見
4.5 Research Agent 深度研究
什麼是 Research Agent?
Research Agent(1.123 Preview)是專為深度研究設計的 Agent 類型。它不會修改程式碼,而是透過分析 Codebase、GitHub Repository 和網頁資訊,產出結構完整、有引用來源的 Markdown 研究報告。
使用方式
在 Copilot CLI(Local)Session 中輸入:
/research Spring Boot 3.4 的 Virtual Threads 最佳實務,
包含效能基準、已知限制、以及在 Web MVC 和 WebFlux 中的差異適用場景
| 場景 | 範例 |
|---|---|
| 技術選型 | /research 比較 Kafka vs RabbitMQ 在銀行交易系統的適用性 |
| API 理解 | /research 分析專案中 SecurityConfig 的認證流程 |
| 方案評估 | /research 將單體架構拆分為微服務的風險與策略 |
| 學習調研 | /research Java 21 的 Pattern Matching 和 Record Pattern 用法 |
| 合規研究 | /research OWASP Top 10 2025 變更重點與 Spring Boot 對應措施 |
Research Agent vs Chat 差異
| 面向 | Research Agent | Chat |
|---|---|---|
| 目標 | 深度報告 | 快速問答 |
| 輸出 | 結構化 Markdown + 引用 | 簡短回覆 |
| 權限 | 唯讀(不修改程式碼) | 可讀寫(Agent Mode) |
| 時間 | 數分鐘(深度搜尋) | 秒級回應 |
| 資料來源 | Codebase + GitHub + Web | 主要是 Codebase |
🏦 企業實務建議:
- 技術選型前使用 Research Agent 產出調研報告,作為決策依據
- 新成員 Onboarding 時使用
/research 分析這個專案的架構與關鍵模組- Research Agent 報告可直接作為技術文件草稿
4.6 Prompt Engineering
好 Prompt 範例 ✅
| # | Prompt | 為何好 |
|---|---|---|
| 1 | 建立一個 Spring Boot REST Controller,路徑 /api/v1/customers,包含 CRUD、分頁查詢,使用 @Valid 驗證,回傳統一的 ApiResponse 格式 | 具體、明確、有約束 |
| 2 | 參考 #file:AccountService.java 的風格,建立 CustomerService,包含查詢、新增、修改功能,使用 @Transactional | 提供參考檔案、風格一致 |
| 3 | 為 TransferService.transfer 方法寫測試,覆蓋以下場景:正常轉帳、餘額不足、帳戶凍結、同帳戶轉帳,使用 JUnit 5 + Mockito | 列出測試場景、指定工具 |
| 4 | 將這段程式碼重構為 Strategy Pattern,每種交易類型是一個 Strategy,需符合 OCP 原則 | 指定設計模式、有原則約束 |
壞 Prompt 範例 ❌
| # | Prompt | 問題 |
|---|---|---|
| 1 | 寫一個 Controller | 太模糊,缺乏上下文 |
| 2 | 幫我 fix | 沒有說明問題、沒有提供程式碼 |
| 3 | 建立整個專案 | 範圍太大,產出品質差 |
| 4 | 寫一段很好的程式 | 「很好」是主觀的,沒有具體標準 |
提升生成品質的技巧
graph LR
A[🎯 明確目標] --> B[📋 提供上下文]
B --> C[🔒 設定約束]
C --> D[📐 指定風格]
D --> E[🔄 迭代優化]
style A fill:#3b82f6,color:#fff
style B fill:#22c55e,color:#fff
style C fill:#f59e0b,color:#fff
style D fill:#ef4444,color:#fff
style E fill:#8b5cf6,color:#fff技巧清單:
- 使用
copilot-instructions.md— 在.github/copilot-instructions.md中定義專案標準:
# Copilot 專案指引
## 程式碼風格
- 使用 Java 21 語法特徵(Record、Pattern Matching、Sealed Class)
- 方法必須有 JavaDoc 註解
- 使用 Lombok 減少 Boilerplate
- 日誌使用 Slf4j(@Slf4j)
## 架構規範
- Controller 只負責接收請求和回傳回應
- 業務邏輯放在 Service 層
- 使用 DTO 傳輸資料,禁止直接回傳 Entity
- 使用 @Transactional 管理事務
## 測試規範
- 使用 JUnit 5 + Mockito
- 測試方法命名:should_預期結果_when_條件
- 每個 Service 方法至少 2 個測試案例
## 安全規範
- 禁止在日誌中記錄密碼或個資
- SQL 查詢使用參數化,禁止字串拼接
- API 必須有輸入驗證(@Valid)- 善用
#file參考 — 讓 Copilot 參考既有程式碼風格:
參考 #file:AccountController.java 的風格,
建立 TransactionController,路徑 /api/v1/transactions- 分步驟請求 — 複雜任務拆解為小步驟:
步驟 1:先建立 Transaction Entity
步驟 2:建立 TransactionRepository(含自訂查詢)
步驟 3:建立 TransactionService(含轉帳邏輯)
步驟 4:建立 TransactionController
步驟 5:建立對應的測試- 使用 Agent Mode — 讓 Copilot 自主完成多檔案任務:
@workspace 使用 Agent Mode,幫我建立完整的「客戶管理模組」(Customer),
包含 Entity、Repository、Service、Controller、DTO、測試,
放在 com.bank.demo.customer 套件下🏦 企業實務注意:
- Copilot 生成的程式碼必須經過人工審查,特別是商業邏輯和安全相關程式碼
- 建議在 Code Review 中加入「AI 生成程式碼標記」,讓審查者特別注意
- 敏感資料(如客戶個資、帳號)不應出現在 Prompt 中
4.7 MCP Server 整合
什麼是 MCP?
Model Context Protocol(MCP) 是一套開放協議,讓 AI Agent 透過標準化介面存取外部工具與資料源。VS Code 自 1.113 起支援 MCP,1.124 新增 OAuth 驗證、企業 XAA 認證與白名單管控。
MCP 架構
flowchart LR
subgraph "VS Code"
AGENT[🤖 Agent Mode]
CLI[💻 CLI Agent]
end
subgraph "MCP Servers"
DB[🗄️ Database MCP<br/>查詢 DB Schema]
GH[🐙 GitHub MCP<br/>Issue / PR 操作]
JIRA[📋 Jira MCP<br/>任務管理]
K8S[☸️ K8s MCP<br/>叢集操作]
CUSTOM[🔧 自訂 MCP<br/>企業內部系統]
end
AGENT --> DB
AGENT --> GH
AGENT --> JIRA
CLI --> DB
CLI --> GH
CLI --> K8S
AGENT --> CUSTOM
style AGENT fill:#3b82f6,color:#fff
style CLI fill:#f59e0b,color:#fff設定 MCP Server
在 .vscode/settings.json 或 User Settings 中設定:
{
"mcp": {
"servers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${env:GITHUB_TOKEN}"
}
},
"database": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "postgresql://localhost:5432/bankingdb"
}
}
}
}
}搭配 Agent Mode 使用
# 在 Agent Mode 中,Agent 可自動呼叫 MCP Server
查詢 bankingdb 的 accounts 表結構,
然後根據結構建立對應的 JPA Entity 和 RepositoryAgent 會透過 Database MCP 取得 Schema,再生成對應的 Java 程式碼。
MCP OAuth 認證(VS Code 1.123)
MCP Server 可透過 OAuth 進行身份驗證。在 mcp.json 中設定:
{
"my-mcp-server": {
"url": "https://mcp.example.com/mcp",
"type": "http",
"oauth": {
"clientId": "your-client-id"
}
}
}Client Secret 安全儲存:
- 當
oauth.clientId設定後,mcp.json上方出現Set Client SecretCodeLens - 點擊後 Secret 儲存於 OS 加密儲存(Credential Manager / Keychain)
- Secret 以 Server URL + Client ID 為範圍隔離
- 隨時可透過 CodeLens 查看 / 替換 / 刪除 Secret
企業級 MCP 認證(Enterprise-Managed,1.123 Preview)
對於採用集中式身份提供者(Entra、Okta、Auth0)的企業,可透過 Cross-App Authorization(XAA) 實現單次登入:
sequenceDiagram
participant DEV as 👨💻 開發者
participant VSCODE as VS Code
participant IDP as 🏢 企業 IdP<br/>(Entra/Okta)
participant MCP as 🔧 MCP Server
DEV->>VSCODE: 啟動 MCP Server
VSCODE->>IDP: 使用企業帳號登入(一次)
IDP-->>VSCODE: ID Token
VSCODE->>MCP: ID-JAG 斷言交換
MCP-->>VSCODE: Resource Access Token
VSCODE->>MCP: 使用 Token 存取 MCP 資源設定方式:
- 管理員透過 MDM 政策設定 IdP:
mcp.enterpriseManagedAuth.idp - 個別 MCP Server 在
oauth區塊加入"enterpriseManaged": true - VS Code 自動走 XAA 流程,無需每個 Server 個別註冊
🏦 企業安全注意:
- MCP Server 可存取外部系統,務必確認網路白名單與權限設定
- 不要在 MCP 設定中直接寫入密碼,使用
${env:VAR}或 OAuth 機制- 建議由資安團隊審核 MCP Server 清單後再部署
- 企業環境優先採用 Enterprise-Managed MCP 認證,統一身份管理
- 透過
chat.plugins.enabledPlugins政策控制可用 MCP Plugin 白名單- 使用
chat.plugins.strictMarketplaces限制僅允許政策核准的 Marketplace
4.8 Custom Instructions / Agent Skills / Custom Agents
自訂化層級總覽
graph TB
subgraph "自訂化層級(由通用到專精)"
A[Custom Instructions<br/>copilot-instructions.md<br/>全域規則] --> B[Prompt Files<br/>.prompt.md<br/>可複用的 Prompt 範本]
B --> C[Agent Skills<br/>.skill.md<br/>特定能力定義]
C --> D[Custom Agents<br/>.agent.md<br/>完整 AI 角色]
end
style A fill:#3b82f6,color:#fff
style B fill:#22c55e,color:#fff
style C fill:#f59e0b,color:#fff
style D fill:#ef4444,color:#fffPrompt Files(.prompt.md)
可複用的 Prompt 範本,在 Chat 中以 / 選擇使用:
---
# .github/prompts/code-review.prompt.md
description: "Spring Boot Code Review Prompt"
---
請根據以下檢查清單進行 Code Review:
1. **安全性**:是否有 SQL Injection、XSS、CSRF 風險?
2. **效能**:是否有 N+1 查詢、不必要的資料庫呼叫?
3. **交易管理**:@Transactional 範圍是否正確?
4. **例外處理**:是否有適當的錯誤處理和回傳格式?
5. **日誌**:是否記錄足夠的操作日誌?
6. **測試覆蓋**:是否有對應的單元測試?Agent Skills(.skill.md)
定義 Agent 的特定能力:
---
# .github/skills/db-migration.skill.md
name: db-migration
description: "產生 Flyway DB Migration 腳本"
tools:
- read_file
- create_file
- run_in_terminal
---
當需要修改資料庫結構時:
1. 讀取現有的 Entity 定義
2. 比對目標結構
3. 產生 Flyway V{timestamp}__description.sql
4. 將 SQL 放在 src/main/resources/db/migration/
5. 執行 mvn flyway:migrate 驗證Custom Agents(.agent.md)
建立專門的 AI 角色:
---
# .github/agents/security-auditor.agent.md
name: security-auditor
description: "專業安全審計 Agent"
tools:
- semantic_search
- read_file
- grep_search
---
你是一位專業的 Java Web 應用安全審計專家。
## 審計範圍
1. OWASP Top 10 檢查
2. Spring Security 設定審查
3. 敏感資料保護(PII、密碼、金鑰)
4. JWT Token 安全性
5. API Rate Limiting
## 輸出格式
產生結構化的安全報告,包含:
- 風險等級(Critical/High/Medium/Low)
- 問題描述
- 影響範圍
- 修復建議
- 程式碼範例Chat Customizations Editor 管理
VS Code 的 Chat Customizations Editor(1.125 GA)提供統一的 UI 管理所有自訂化檔案:
Ctrl + Shift + P → Chat: Open Chat Customizations
在此介面中可以:
- 瀏覽所有 Custom Instructions、Prompt Files、Agent Skills、Custom Agents
- 啟用 / 停用特定檔案
- 新增 / 編輯 / 刪除
5. 專案架構設計(企業級)
5.1 Clean Architecture / Hexagonal Architecture
Clean Architecture 分層圖
graph TB
subgraph "外層 - Frameworks & Drivers"
WEB[🌐 Web Framework<br/>Spring MVC]
DB[🗄️ Database<br/>JPA / JDBC]
EXT[🔌 External Services<br/>API / MQ]
end
subgraph "介面層 - Interface Adapters"
CTRL[🎯 Controllers<br/>REST API]
REPO[💾 Repository Impl<br/>JPA Implementation]
GW[🔗 Gateway Impl<br/>HTTP Client]
end
subgraph "應用層 - Application Business Rules"
UC[⚙️ Use Cases<br/>Application Services]
PORT[📋 Ports<br/>Repository Interface]
end
subgraph "核心層 - Enterprise Business Rules"
ENT[🏛️ Entities<br/>Domain Objects]
VO[📦 Value Objects]
RULE[📐 Business Rules]
end
WEB --> CTRL
CTRL --> UC
UC --> PORT
UC --> ENT
PORT -.-> REPO
REPO --> DB
UC -.-> GW
GW --> EXT
style ENT fill:#ef4444,color:#fff
style UC fill:#f59e0b,color:#fff
style CTRL fill:#3b82f6,color:#fff
style REPO fill:#22c55e,color:#fff企業級套件結構
com.bank.demo/
├── domain/ ← 核心領域(不依賴任何框架)
│ ├── model/
│ │ ├── Account.java ← Domain Entity(非 JPA Entity)
│ │ ├── Transaction.java
│ │ └── Money.java ← Value Object
│ ├── repository/
│ │ └── AccountRepository.java ← Port(純 Interface)
│ ├── service/
│ │ └── TransferDomainService.java ← Domain Service
│ └── exception/
│ └── DomainException.java
│
├── application/ ← 應用層(Use Cases)
│ ├── usecase/
│ │ ├── CreateAccountUseCase.java
│ │ ├── TransferMoneyUseCase.java
│ │ └── QueryAccountUseCase.java
│ ├── dto/
│ │ ├── command/
│ │ │ ├── CreateAccountCommand.java
│ │ │ └── TransferCommand.java
│ │ └── query/
│ │ └── AccountQuery.java
│ └── port/
│ └── output/
│ └── NotificationPort.java ← 對外通知介面
│
├── infrastructure/ ← 基礎設施層
│ ├── persistence/
│ │ ├── entity/
│ │ │ └── AccountJpaEntity.java ← JPA Entity(資料庫映射)
│ │ ├── repository/
│ │ │ └── AccountJpaRepository.java
│ │ └── adapter/
│ │ └── AccountRepositoryAdapter.java ← Port 實作
│ ├── config/
│ │ ├── SecurityConfig.java
│ │ └── JpaConfig.java
│ └── external/
│ └── NotificationAdapter.java ← 通知實作(Email/SMS)
│
└── presentation/ ← 展示層
├── controller/
│ └── AccountController.java
├── dto/
│ ├── request/
│ │ └── CreateAccountRequest.java
│ └── response/
│ └── AccountResponse.java
└── advice/
└── GlobalExceptionHandler.java5.2 分層設計
各層依賴規則
graph LR
A[Presentation] -->|依賴| B[Application]
B -->|依賴| C[Domain]
D[Infrastructure] -->|依賴| B
D -->|實作| C
C -.->|不依賴任何層| C
style C fill:#ef4444,color:#fff
style B fill:#f59e0b,color:#fff
style A fill:#3b82f6,color:#fff
style D fill:#22c55e,color:#fff核心原則:依賴只能由外向內,Domain 層不依賴任何外部框架。
範例:Transfer Use Case
package com.bank.demo.application.usecase;
import com.bank.demo.application.dto.command.TransferCommand;
import com.bank.demo.domain.exception.DomainException;
import com.bank.demo.domain.model.Account;
import com.bank.demo.domain.model.Money;
import com.bank.demo.domain.repository.AccountRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 轉帳 Use Case
* <p>
* 職責:協調領域物件完成轉帳流程
* </p>
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class TransferMoneyUseCase {
private final AccountRepository accountRepository;
@Transactional
public void execute(TransferCommand command) {
log.info("執行轉帳: {} -> {}, 金額: {}",
command.fromAccountNumber(),
command.toAccountNumber(),
command.amount());
Account from = accountRepository
.findByAccountNumber(command.fromAccountNumber())
.orElseThrow(() -> new DomainException("轉出帳戶不存在"));
Account to = accountRepository
.findByAccountNumber(command.toAccountNumber())
.orElseThrow(() -> new DomainException("轉入帳戶不存在"));
Money transferAmount = Money.of(command.amount());
// 領域邏輯在 Domain Entity 中
from.withdraw(transferAmount);
to.deposit(transferAmount);
accountRepository.save(from);
accountRepository.save(to);
log.info("轉帳完成");
}
}Domain Entity(含業務邏輯)
package com.bank.demo.domain.model;
import com.bank.demo.domain.exception.DomainException;
/**
* 帳戶領域物件
*/
public class Account {
private Long id;
private String accountNumber;
private String accountName;
private Money balance;
private AccountStatus status;
/**
* 存款
*/
public void deposit(Money amount) {
if (amount.isNegativeOrZero()) {
throw new DomainException("存款金額必須大於零");
}
this.balance = this.balance.add(amount);
}
/**
* 提款
*/
public void withdraw(Money amount) {
if (amount.isNegativeOrZero()) {
throw new DomainException("提款金額必須大於零");
}
if (this.status != AccountStatus.ACTIVE) {
throw new DomainException("帳戶非啟用狀態,無法提款");
}
if (this.balance.isLessThan(amount)) {
throw new DomainException("餘額不足,目前餘額: " + this.balance);
}
this.balance = this.balance.subtract(amount);
}
/**
* 凍結帳戶
*/
public void freeze() {
this.status = AccountStatus.FROZEN;
}
public enum AccountStatus {
ACTIVE, FROZEN, CLOSED
}
}5.3 DTO / VO / Entity 分離
三者差異
| 類型 | 用途 | 位置 | 特性 |
|---|---|---|---|
| Entity | 資料庫映射 | Infrastructure | JPA 註解、可變 |
| Domain Entity | 領域模型 | Domain | 包含業務邏輯、無框架依賴 |
| DTO | 資料傳輸 | Presentation / Application | 不含邏輯、序列化 |
| VO(Value Object) | 值物件 | Domain | 不可變、值相等性 |
Value Object 範例
package com.bank.demo.domain.model;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
/**
* 金額值物件(不可變)
*
* @param amount 金額
* @param currency 幣別
*/
public record Money(BigDecimal amount, String currency) {
public static final String DEFAULT_CURRENCY = "TWD";
public Money {
Objects.requireNonNull(amount, "金額不可為 null");
Objects.requireNonNull(currency, "幣別不可為 null");
amount = amount.setScale(2, RoundingMode.HALF_UP);
}
public static Money of(BigDecimal amount) {
return new Money(amount, DEFAULT_CURRENCY);
}
public static Money zero() {
return new Money(BigDecimal.ZERO, DEFAULT_CURRENCY);
}
public Money add(Money other) {
validateCurrency(other);
return new Money(this.amount.add(other.amount), this.currency);
}
public Money subtract(Money other) {
validateCurrency(other);
return new Money(this.amount.subtract(other.amount), this.currency);
}
public boolean isNegativeOrZero() {
return amount.compareTo(BigDecimal.ZERO) <= 0;
}
public boolean isLessThan(Money other) {
validateCurrency(other);
return amount.compareTo(other.amount) < 0;
}
private void validateCurrency(Money other) {
if (!this.currency.equals(other.currency)) {
throw new IllegalArgumentException(
"幣別不一致: " + this.currency + " vs " + other.currency);
}
}
}Request/Response DTO 範例(使用 Java Record)
// Request DTO
package com.bank.demo.presentation.dto.request;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
/**
* 建立帳戶請求
*/
public record CreateAccountRequest(
@NotBlank(message = "帳號不可為空")
@Pattern(regexp = "^\\d{4}-\\d{4}-\\d{4}$", message = "帳號格式錯誤")
String accountNumber,
@NotBlank(message = "戶名不可為空")
@Size(max = 50, message = "戶名長度不可超過 50")
String accountName,
@NotNull(message = "金額不可為空")
@DecimalMin(value = "0", message = "金額不可為負數")
BigDecimal balance,
@NotBlank(message = "帳戶類型不可為空")
String accountType
) {}// Response DTO
package com.bank.demo.presentation.dto.response;
import lombok.Builder;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 帳戶回應
*/
@Builder
public record AccountResponse(
String accountNumber,
String accountName,
BigDecimal balance,
String accountType,
LocalDateTime createdAt
) {}🏦 企業實務建議:
- 絕對不要將 JPA Entity 直接作為 API Response 回傳
- 使用 MapStruct 或手動 Mapper 做 Entity ↔ DTO 轉換
- Value Object 使用 Java Record(Java 16+),天然不可變
6. 安全性與最佳實務
6.1 Spring Security 基本設定
SecurityConfig.java
package com.bank.demo.infrastructure.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* Spring Security 設定
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthFilter;
public SecurityConfig(JwtAuthenticationFilter jwtAuthFilter) {
this.jwtAuthFilter = jwtAuthFilter;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable()) // REST API 不需 CSRF
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
// 開放端點
.requestMatchers("/api/v1/auth/**").permitAll()
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/h2-console/**").permitAll()
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
// 需認證
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthFilter,
UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12); // 強度 12
}
}6.2 API 驗證(JWT)
JWT 工具類
package com.bank.demo.infrastructure.security;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
/**
* JWT 工具類
*/
@Component
public class JwtTokenProvider {
private final SecretKey secretKey;
private final long expirationMs;
public JwtTokenProvider(
@Value("${app.jwt.secret}") String secret,
@Value("${app.jwt.expiration-ms:3600000}") long expirationMs) {
this.secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
this.expirationMs = expirationMs;
}
/**
* 產生 JWT Token
*/
public String generateToken(String username, String role) {
Date now = new Date();
Date expiry = new Date(now.getTime() + expirationMs);
return Jwts.builder()
.subject(username)
.claim("role", role)
.issuedAt(now)
.expiration(expiry)
.signWith(secretKey)
.compact();
}
/**
* 解析 Token 取得使用者名稱
*/
public String getUsernameFromToken(String token) {
return parseClaims(token).getSubject();
}
/**
* 驗證 Token 是否有效
*/
public boolean validateToken(String token) {
try {
parseClaims(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
private Claims parseClaims(String token) {
return Jwts.parser()
.verifyWith(secretKey)
.build()
.parseSignedClaims(token)
.getPayload();
}
}JWT Filter
package com.bank.demo.infrastructure.security;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
/**
* JWT 認證過濾器
*/
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider tokenProvider;
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = extractToken(request);
if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) {
String username = tokenProvider.getUsernameFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String extractToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}6.3 Copilot 生成程式碼的安全檢查
安全檢查清單
Copilot 生成的程式碼必須檢查以下項目:
| # | 檢查項目 | 風險 | 檢查方式 |
|---|---|---|---|
| 1 | SQL Injection | 🔴 高 | 確認使用參數化查詢,禁止字串拼接 SQL |
| 2 | XSS | 🔴 高 | 確認輸出已編碼 / 使用 @Valid 驗證輸入 |
| 3 | 敏感資料外洩 | 🔴 高 | 日誌不可記錄密碼、身分證字號、信用卡號 |
| 4 | 硬編碼密碼 | 🟡 中 | 敏感值使用環境變數或 Vault |
| 5 | 不安全的依賴 | 🟡 中 | 使用 mvn dependency-check:check 掃描 |
| 6 | 過度暴露 API | 🟡 中 | 確認端點授權設定正確 |
| 7 | 未處理的例外 | 🟡 中 | 使用 @ControllerAdvice 統一處理 |
| 8 | 不安全的隨機數 | 🟡 中 | 使用 SecureRandom 取代 Random |
常見 Copilot 安全陷阱
// ❌ Copilot 可能產生的不安全程式碼
@Query("SELECT a FROM Account a WHERE a.name = '" + name + "'") // SQL Injection!
List<Account> findByName(String name);
// ✅ 正確寫法
@Query("SELECT a FROM Account a WHERE a.name = :name")
List<Account> findByName(@Param("name") String name);
// ❌ 密碼寫在程式碼中
String dbPassword = "P@ssw0rd123";
// ✅ 使用環境變數
@Value("${spring.datasource.password}")
String dbPassword;
// ❌ 日誌記錄敏感資料
log.info("使用者登入: password={}", password);
// ✅ 隱藏敏感資料
log.info("使用者登入: userId={}", userId);🏦 企業實務注意:
- 所有 Copilot 生成的程式碼,在 PR 階段必須通過 SonarQube 掃描
- 金融系統必須使用 OWASP Dependency-Check 檢查第三方元件弱點
- JWT Secret 建議至少 256 bits,存放在 Vault 或 K8s Secret
- 善用 4.6 Custom Agents 建立
security-auditorAgent,在 Agent Mode 中自動進行安全審查- 可透過 MCP Server 串接企業 SAST/DAST 工具(如 Fortify、Checkmarx),讓 Agent 自動取得掃描結果
7. 測試與除錯
7.1 單元測試(JUnit 5)
測試結構
src/test/java/
└── com/bank/demo/
├── domain/
│ ├── model/
│ │ ├── AccountTest.java ← Domain Entity 測試
│ │ └── MoneyTest.java ← Value Object 測試
│ └── service/
│ └── TransferDomainServiceTest.java
├── application/
│ └── usecase/
│ └── TransferMoneyUseCaseTest.java ← Use Case 測試(Mock)
├── presentation/
│ └── controller/
│ └── AccountControllerTest.java ← API 測試(MockMvc)
└── integration/
└── AccountIntegrationTest.java ← 整合測試Controller 測試範例(MockMvc)
package com.bank.demo.presentation.controller;
import com.bank.demo.application.usecase.QueryAccountUseCase;
import com.bank.demo.presentation.dto.response.AccountResponse;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import java.math.BigDecimal;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(AccountController.class)
@DisplayName("帳戶 API 測試")
class AccountControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private QueryAccountUseCase queryAccountUseCase;
@Test
@WithMockUser(username = "testuser", roles = {"USER"})
@DisplayName("GET /api/v1/accounts/{id} - 查詢帳戶成功")
void shouldReturnAccountWhenExists() throws Exception {
// Given
AccountResponse response = AccountResponse.builder()
.accountNumber("0001-2345-6789")
.accountName("王小明")
.balance(new BigDecimal("100000.00"))
.accountType("SAVINGS")
.build();
when(queryAccountUseCase.findByAccountNumber("0001-2345-6789"))
.thenReturn(response);
// When & Then
mockMvc.perform(get("/api/v1/accounts/0001-2345-6789")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.accountNumber").value("0001-2345-6789"))
.andExpect(jsonPath("$.accountName").value("王小明"))
.andExpect(jsonPath("$.balance").value(100000.00));
}
@Test
@DisplayName("GET /api/v1/accounts/{id} - 未認證應回傳 401")
void shouldReturn401WhenNotAuthenticated() throws Exception {
mockMvc.perform(get("/api/v1/accounts/0001-2345-6789"))
.andExpect(status().isUnauthorized());
}
}使用 Copilot 快速生成測試
# 在 Copilot Chat 中:
@workspace /tests 為 #file:TransferMoneyUseCase.java 產生完整的測試,
使用 JUnit 5 + Mockito,涵蓋以下場景:
- 正常轉帳(驗證餘額變化)
- 轉出帳戶不存在
- 轉入帳戶不存在
- 餘額不足
- 金額為零或負數
- 同帳戶轉帳7.2 API 測試
REST Client 測試檔案
建立 test/http/account-api.http:
@baseUrl = http://localhost:8080/api/v1
@token = {{login.response.body.token}}
### 登入取得 Token
# @name login
POST {{baseUrl}}/auth/login
Content-Type: application/json
{
"username": "admin",
"password": "admin123"
}
### 查詢所有帳戶
GET {{baseUrl}}/accounts
Authorization: Bearer {{token}}
### 建立帳戶
POST {{baseUrl}}/accounts
Authorization: Bearer {{token}}
Content-Type: application/json
{
"accountNumber": "0001-2345-6789",
"accountName": "王小明",
"balance": 100000.00,
"accountType": "SAVINGS"
}
### 轉帳
POST {{baseUrl}}/accounts/transfer
Authorization: Bearer {{token}}
Content-Type: application/json
{
"fromAccount": "0001-2345-6789",
"toAccount": "0002-3456-7890",
"amount": 30000.00,
"remark": "租金轉帳"
}7.3 VS Code Debug 技巧
launch.json 設定
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Spring Boot - 啟動",
"request": "launch",
"mainClass": "com.bank.demo.BankingApiApplication",
"projectName": "banking-api",
"args": "--spring.profiles.active=dev",
"vmArgs": "-Xmx512m -Xms256m"
},
{
"type": "java",
"name": "Spring Boot - Debug(遠端)",
"request": "attach",
"hostName": "localhost",
"port": 5005
},
{
"type": "java",
"name": "JUnit Test - 當前檔案",
"request": "launch",
"mainClass": "",
"projectName": "banking-api"
}
]
}Debug 常用操作
| 操作 | 快捷鍵 | 說明 |
|---|---|---|
| 設定中斷點 | F9 | 在指定行設定/取消中斷點 |
| 開始除錯 | F5 | 啟動 Debug Session |
| 逐步執行 | F10 | Step Over(不進入方法) |
| 進入方法 | F11 | Step Into(進入方法) |
| 跳出方法 | Shift + F11 | Step Out(跳出目前方法) |
| 繼續執行 | F5 | Continue(到下一個中斷點) |
| 條件中斷點 | 右鍵 > Conditional Breakpoint | 指定條件才停住 |
條件中斷點範例
// 在 transfer 方法中設定條件中斷點:
// 條件:amount.compareTo(new BigDecimal("50000")) > 0
// 只有轉帳金額超過 5 萬時才停住
public void transfer(TransferRequest request) {
BigDecimal amount = request.getAmount(); // ← 在此設定條件中斷點
// ...
}7.4 使用 Copilot 協助除錯
方法 1:選取錯誤訊息,使用 Chat
/fix 我收到以下錯誤:
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'accountController':
Unsatisfied dependency expressed through constructor parameter 0方法 2:選取程式碼,請 Copilot 解釋
/explain 為什麼這段程式碼會產生 NullPointerException?
#selection方法 3:使用 Copilot 的 Fix 功能
- 當 VS Code 顯示紅色底線錯誤時
- 點擊燈泡圖示(💡)
- 選擇 Fix using Copilot
- Copilot 會自動修正
🏦 企業實務建議:
- 銀行系統建議單元測試覆蓋率至少 80%
- 金融核心邏輯(如利率計算、轉帳)需達 95% 覆蓋率
- 使用 JaCoCo 產生覆蓋率報告,整合到 CI/CD
7.5 整合式瀏覽器 Agent 測試
VS Code 的整合式瀏覽器(Integrated Browser)在 1.123–1.126 期間持續強化,現已成為完整的開發內瀏覽器,可與 Agent Mode 結合實現半自動化 E2E 測試。
整合式瀏覽器完整功能一覽(截至 1.126)
| 功能 | 版本 | 說明 |
|---|---|---|
| 自簽憑證支援 | 1.113 | HTTPS 開發自動信任本地憑證 |
| Quick Open Tab | 1.113 | Ctrl+Shift+A 快速搜尋分頁 |
| 頁面收藏 | 1.123 | 星號收藏常用頁面,URL 列快速存取 |
| 區域截圖 | 1.123 | 選取區域截圖加入 Chat 上下文 |
| 全頁面截圖 | 1.123 | 超出視窗的完整頁面截圖(實驗性) |
| 瀏覽歷史 | 1.124 | 自動記錄,URL 列顯示歷史建議,Ctrl+H 管理 |
| 工具列自訂 | 1.124 | 右鍵工具列自訂永久顯示的按鈕 |
| 快速文字輸入 | 1.124 | Agent typeInPage 支援 submit 參數 |
| 網址列搜尋 | 1.125 | 輸入關鍵字直接搜尋(可設定搜尋引擎) |
| Remote Proxy | 1.125 | 遠端工作區中代理 HTTP(S) 流量 |
| Forwarded Port 整合 | 1.125 | Agent 自動重寫已轉發的 Port URL |
使用場景
flowchart LR
AGENT[🤖 Agent Mode] -->|1. 修改程式碼| CODE[📝 Java 程式碼]
AGENT -->|2. 重啟應用| TERM[💻 終端<br/>mvn spring-boot:run]
AGENT -->|3. 開啟瀏覽器| BROWSER[🌐 整合式瀏覽器<br/>localhost:8080]
AGENT -->|4. 驗證結果| CHECK[✅ 檢查回應]
CHECK -->|5. 失敗則修正| AGENT
style AGENT fill:#3b82f6,color:#fff
style BROWSER fill:#22c55e,color:#fffAgent + 瀏覽器測試 Prompt 範例
在 Agent Mode 中:
修改 AccountController 的 /api/v1/accounts 端點,
新增分頁查詢功能(支援 page 和 size 參數),
修改完成後:
1. 重新啟動 Spring Boot
2. 使用整合式瀏覽器存取 http://localhost:8080/api/v1/accounts?page=0&size=10
3. 確認回傳結果包含 totalElements 欄位自簽憑證支援
開發 HTTPS 應用時,整合式瀏覽器支援信任自簽憑證:
# application.yml - 啟用 HTTPS
server:
ssl:
key-store: classpath:keystore.p12
key-store-password: ${SSL_PASSWORD}
key-store-type: PKCS12瀏覽器會自動信任本地開發用的自簽憑證,不再出現安全警告。
8. CI/CD 與版本控管
8.1 Git 基本流程
Branch Strategy(Git Flow 簡化版)
gitGraph
commit id: "init"
branch develop
checkout develop
commit id: "feature-base"
branch feature/account-api
commit id: "add entity"
commit id: "add service"
commit id: "add controller"
checkout develop
merge feature/account-api id: "merge account-api"
branch feature/transfer
commit id: "transfer logic"
commit id: "transfer test"
checkout develop
merge feature/transfer id: "merge transfer"
checkout main
merge develop id: "release v1.0" tag: "v1.0"分支命名規範
| 分支類型 | 格式 | 範例 |
|---|---|---|
| 功能 | feature/<jira-id>-<description> | feature/BANK-123-account-api |
| 修復 | bugfix/<jira-id>-<description> | bugfix/BANK-456-transfer-error |
| 緊急修復 | hotfix/<jira-id>-<description> | hotfix/BANK-789-security-patch |
| 發布 | release/<version> | release/1.2.0 |
Commit Message 規範
<type>(<scope>): <subject>
<body>
<footer>Type:feat | fix | refactor | test | docs | chore
範例:
feat(account): 新增轉帳 API
- 實作 /api/v1/accounts/transfer 端點
- 包含餘額檢查與交易紀錄
- 新增對應的 JUnit 測試
Closes BANK-1238.2 GitHub Actions 基本 CI/CD
CI Pipeline(.github/workflows/ci.yml)
name: CI Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: 取得原始碼
uses: actions/checkout@v4
- name: 設定 JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: 'maven'
- name: 編譯
run: mvn compile -B
- name: 執行測試
run: mvn test -B
- name: 產生測試覆蓋率報告
run: mvn jacoco:report -B
- name: 上傳覆蓋率報告
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: target/site/jacoco/
- name: 安全掃描(OWASP Dependency-Check)
run: mvn dependency-check:check -B
continue-on-error: true
- name: 封裝 JAR
run: mvn package -DskipTests -B
- name: 上傳 JAR
uses: actions/upload-artifact@v4
with:
name: app-jar
path: target/*.jar
code-quality:
runs-on: ubuntu-latest
needs: build-and-test
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # SonarQube 需要完整歷史
- name: 設定 JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: 'maven'
- name: SonarQube 掃描
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
mvn sonar:sonar \
-Dsonar.host.url=${{ secrets.SONAR_HOST_URL }} \
-Dsonar.token=${{ secrets.SONAR_TOKEN }}8.3 Copilot 協助產生 Pipeline
在 Copilot Chat 中:
@workspace 幫我產生 GitHub Actions workflow,需求如下:
1. 觸發條件:push 到 main/develop、PR 到 main/develop
2. 步驟:
- 使用 JDK 21 + Maven
- 編譯 → 測試 → JaCoCo 覆蓋率報告
- OWASP Dependency-Check
- SonarQube 掃描
- 打包 JAR 並上傳 Artifact
3. 環境變數使用 GitHub Secrets
4. 加入 Maven Cache 加速🏦 企業實務注意:
- 銀行系統通常有多環境部署:DEV → SIT → UAT → PROD
- PR 合併前必須通過:CI 測試 + SonarQube 品質閘門 + 至少 2 人 Code Review
- 正式環境部署需加入人工審核(Manual Approval)
8.4 Cloud Agent 與 PR 協作(Preview)
Cloud Agent 自動處理 Issue
Cloud Agent 可直接從 GitHub Issue 開始工作,在雲端完成開發後開 PR:
sequenceDiagram
participant DEV as 🧑💻 開發者
participant GH as 🐙 GitHub Issue
participant CA as ☁️ Cloud Agent
participant PR as 📋 Pull Request
participant REV as 👀 Code Review
DEV->>GH: 建立 Issue(描述需求)
DEV->>CA: 指派 Cloud Agent 處理
CA->>CA: 建立分支
CA->>CA: 分析 Codebase
CA->>CA: 編碼 & 測試
CA->>PR: 自動建立 PR
PR->>DEV: PR 通知
DEV->>REV: Code Review
REV->>PR: 合併 PR使用方式
- 從 VS Code 發起:在 Chat 中
@github 將 Issue #42 指派給 Cloud Agent - 從 GitHub 發起:在 Issue 中
@copilot 處理此 Issue - 監控進度:在 VS Code Sessions View 中查看 Cloud Agent 狀態
Copilot Review
在 PR 中使用 Copilot 進行自動 Code Review:
# 在 PR 頁面或 VS Code PR Extension 中
@copilot 請 review 這個 PR,特別注意:
1. 安全性問題
2. 效能問題
3. 是否符合 copilot-instructions.md 規範🏦 企業實務注意:
- Cloud Agent 的程式碼會在 GitHub 雲端環境執行,請確認合規性
- 建議限制 Cloud Agent 可存取的 Repository 範圍
- Cloud Agent 開的 PR 仍需人工 Review 才能合併
- 可搭配 Branch Protection Rules 強制 Review、CI 通過
9. 系統維護與升級
9.1 VS Code 更新策略
| 策略 | 說明 | 適用場景 |
|---|---|---|
| 自動更新 | VS Code 預設自動更新 | 個人開發、非管控環境 |
| 手動控制 | 關閉自動更新,統一部署 | 企業環境、需驗證相容性 |
| Insider 版本 | 搶先體驗新功能 | 技術評估、測試用途 |
企業更新流程
flowchart LR
A[新版本發布] --> B[技術團隊評估]
B --> C[測試環境驗證]
C --> D{相容性測試通過?}
D -->|是| E[更新技術公告]
E --> F[團隊統一更新]
D -->|否| G[回報問題]
G --> H[等待修復版]
H --> B關閉自動更新(企業建議)
// settings.json
{
"update.mode": "manual",
"extensions.autoUpdate": false,
"extensions.autoCheckUpdates": false
}9.2 Extension 管理
Extension 自動更新策略(VS Code 1.123–1.125)
VS Code 1.123 起引入延遲自動更新機制,1.125 進一步簡化設定:
| 設定 | 值 | 說明 |
|---|---|---|
extensions.autoUpdate | on / off | 啟用或停用自動更新(簡化自舊版 true/false/delayed) |
extensions.autoUpdateDelay | 小時數(預設 2) | 新版發布後等待多久才自動更新 |
延遲機制說明:
- 新版 Extension 發布後,預設等待 2 小時才自動安裝
- 可隨時手動點擊 Update 立即更新
- Microsoft、GitHub、OpenAI 等信任發布者不受延遲限制
- 停用的 Extension 不再自動更新,啟用時才會更新
Extension 版本鎖定
建立 .vscode/extensions.json 統一團隊 Extension:
{
"recommendations": [
"vscjava.vscode-java-pack",
"vmware.vscode-boot-dev-pack",
"github.copilot",
"github.copilot-chat",
"humao.rest-client",
"eamodio.gitlens",
"sonarsource.sonarlint-vscode"
],
"unwantedRecommendations": []
}定期檢查 Extension
# 列出已安裝的 Extension
code --list-extensions --show-versions
# 匯出 Extension 清單
code --list-extensions > extensions-list.txt
# 從清單安裝(團隊統一)
Get-Content extensions-list.txt | ForEach-Object { code --install-extension $_ }9.3 Copilot 模型更新與成本管理
Copilot 可用模型(VS Code 1.126,2026-06-24)
| 模型 | 特點 | 適用場景 | Context Window |
|---|---|---|---|
| GPT-4o | 平衡速度與品質 | 日常編碼(快速模式預設) | 128K |
| GPT-5.5 | 最新一代,推理極強 | 超大型程式碼分析、跨檔案理解 | 1M |
| Claude Sonnet 4.6 | 速度與品質兼顧 | Agent Mode 首選、日常 Chat | 200K |
| Claude Opus 4 | 最高品質 | 關鍵業務邏輯、安全程式碼 | 200K |
| Claude Opus 4.7 | 最新版,支援超大上下文 | 大型 Codebase 分析、Research Agent | 1M |
| o3-mini | 推理能力極強 | 演算法、數學、邏輯推演 | 128K |
1M Context Window(VS Code 1.123)
VS Code 現在支援百萬 Token 上下文視窗,適用於相容的 Anthropic 和 OpenAI 模型(如 Claude Opus 4.7、GPT-5.5)。這使得處理超大型 Codebase 和長時間對話時不會遺失重要上下文。
⚠️ 注意:較大的上下文視窗每次互動消耗更多 Token,在用量計費下會增加 Credits 使用。
統一模型自訂化選擇器(VS Code 1.126)
VS Code 1.126 將 Context Size 和 Thinking Effort(推理深度)合併為單一選擇器:
Model Picker → 選擇模型 → 點擊齒輪圖示
→ 統一選擇器中同時調整:
• Context Size: Default / Large / Extended (1M)
• Thinking Effort: Low / Medium / High⚠️ 棄用通知:以下設定已完全移除:
// ❌ 已移除 - 不要使用 "github.copilot.chat.anthropic.thinking.effort": "...", "github.copilot.chat.responsesApiReasoningEffort": "..."
安裝第三方模型 Provider(VS Code 1.125)
Language Models 編輯器新增 Install Model Providers 按鈕,可直接搜尋並安裝提供模型的 Extension(BYOK 或第三方 Provider)。安裝後模型自動出現在 Model Picker 中。
成本管理與追蹤
VS Code 1.125–1.126 引入完整的成本管理體系:
| 功能 | 版本 | 說明 |
|---|---|---|
| Additional Spend 追蹤 | 1.125 | Copilot Status Dashboard 顯示已使用的額外預算百分比 |
| Session 級別成本 | 1.126 | 查看每個 Chat Session 的 Credits 消耗與 Token 用量 |
flowchart LR
A[開發者使用 Copilot] --> B[Token 消耗]
B --> C[Session 成本追蹤<br/>Credits 即時顯示]
C --> D{超過預算?}
D -->|否| E[繼續使用]
D -->|是| F[通知提醒<br/>調整使用模式]
F --> G[切換較輕量模型<br/>降低 Context Size]
style C fill:#f59e0b,color:#fff
style F fill:#ef4444,color:#fff模型選擇策略矩陣(更新版)
| 場景 | 建議模型 | Context Size | Thinking Effort | 成本考量 |
|---|---|---|---|---|
| 日常編碼補全 | GPT-4o | Default | — | 💰 低 |
| Chat 問答 | Claude Sonnet 4.6 | Default | Medium | 💰 中 |
| Agent Mode 多步驟 | Claude Sonnet 4.6 | Large | High | 💰💰 中高 |
| 架構設計討論 | Claude Opus 4.7 | Extended | High | 💰💰💰 高 |
| 大型 Codebase 分析 | GPT-5.5 | Extended (1M) | High | 💰💰💰 高 |
| 複雜演算法 | o3-mini | Default | 固定高 | 💰💰 中 |
| 安全審查 | Claude Opus 4 | Large | High | 💰💰💰 高 |
| Research Agent | Claude Opus 4.7 | Extended | High | 💰💰💰 高 |
提升 Copilot 效率的設定
{
// 啟用所有語言的 Copilot
"github.copilot.enable": {
"*": true
},
// Agent Mode 啟用
"chat.agent.enabled": true,
// Autopilot 預設權限(企業建議設為 auto-approve)
"chat.permissions.default": "auto-approve",
// Session Sync 啟用
"chat.sessionSync.enabled": true,
// 自訂指令檔
"github.copilot.chat.codeGeneration.instructions": [
{ "file": ".github/copilot-instructions.md" }
]
}🏦 企業實務建議:
- 每季評估一次 VS Code 與 Extension 版本
- 建立「VS Code 標準設定包」,新成員可一鍵匯入
- 日常開發用 GPT-4o(低成本);Code Review 用 Claude Sonnet 4.6;安全審查用 Claude Opus 4
- 啟用 Session 成本追蹤,設定團隊月度 Credits 預算上限
- 善用延遲自動更新(
extensions.autoUpdateDelay),降低問題版本風險- 透過 MDM 政策統一管理 Copilot 設定(
chat.tools.global.autoApprove)
10. 團隊導入建議(企業級)
10.1 開發規範
Java Coding Standard(摘要)
## 命名規範
- 類別:PascalCase(例:AccountService)
- 方法/變數:camelCase(例:findByAccountNumber)
- 常數:UPPER_SNAKE_CASE(例:MAX_TRANSFER_AMOUNT)
- 套件:全小寫(例:com.bank.demo.service)
## 方法規範
- 單一方法不超過 30 行
- 參數不超過 4 個(超過使用 DTO)
- 必須有 JavaDoc 註解
## 例外處理
- Controller 層使用 @ControllerAdvice 統一處理
- Service 層使用自定義 BusinessException
- 禁止吞掉例外(catch 後不處理)10.2 Copilot 使用規範
Copilot 使用指南
| 規則 | 說明 |
|---|---|
| 必須審查 | Copilot 產出的程式碼必須經過人工審查 |
| 禁止盲接 | 不可直接按 Tab 接受所有建議,需理解後再接受 |
| 敏感資料 | 禁止在 Prompt 中輸入客戶個資、帳號、密碼 |
| 商業邏輯 | 核心商業邏輯(如利率計算)需人工撰寫並驗證 |
| 安全掃描 | AI 生成程式碼必須通過 SonarQube 掃描 |
| 測試覆蓋 | AI 生成的程式碼必須有對應測試 |
Copilot 適合與不適合的場景
graph TB
subgraph "✅ 適合使用 Copilot"
A1[CRUD 程式碼]
A2[單元測試生成]
A3[DTO / Entity 轉換]
A4[JavaDoc 生成]
A5[Boilerplate 程式碼]
A6[SQL 查詢語法]
A7[CI/CD Pipeline]
end
subgraph "⚠️ 需謹慎使用"
B1[業務邏輯]
B2[安全驗證]
B3[金融計算]
B4[資料遷移]
end
subgraph "❌ 不適合使用"
C1[密碼 / 金鑰生成]
C2[加密演算法實作]
C3[涉及個資的程式]
end
style A1 fill:#22c55e,color:#fff
style A2 fill:#22c55e,color:#fff
style A3 fill:#22c55e,color:#fff
style A4 fill:#22c55e,color:#fff
style A5 fill:#22c55e,color:#fff
style A6 fill:#22c55e,color:#fff
style A7 fill:#22c55e,color:#fff
style B1 fill:#f59e0b,color:#fff
style B2 fill:#f59e0b,color:#fff
style B3 fill:#f59e0b,color:#fff
style B4 fill:#f59e0b,color:#fff
style C1 fill:#ef4444,color:#fff
style C2 fill:#ef4444,color:#fff
style C3 fill:#ef4444,color:#fff10.3 Code Review 流程
Code Review 檢查重點
flowchart TD
A[提交 PR] --> B{自動檢查}
B -->|CI 通過| C[SonarQube 品質閘門]
B -->|CI 失敗| D[修復後重新提交]
C -->|通過| E[人工 Code Review]
C -->|未通過| D
E --> F{AI 生成程式碼?}
F -->|是| G[🔍 加強審查<br/>安全性 / 正確性 / 效能]
F -->|否| H[一般審查]
G --> I{審查通過?}
H --> I
I -->|通過| J[✅ 合併]
I -->|不通過| K[修改後重新審查]
K --> ECode Review Checklist
- 程式碼是否符合專案架構(Clean Architecture 分層)
- 方法命名是否清楚表達意圖
- 是否有適當的輸入驗證(@Valid)
- 例外處理是否得當
- 是否有足夠的單元測試
- SQL 是否使用參數化查詢
- 日誌是否記錄必要資訊(不含敏感資料)
- 是否有硬編碼的設定值
- AI 生成的程式碼是否理解其邏輯
10.4 AI 輔助開發治理
AI Governance Framework
graph TB
subgraph "治理層 - Governance"
G1[AI 使用政策]
G2[資料保護]
G3[審計追蹤]
end
subgraph "流程層 - Process"
P1[Code Review 流程]
P2[品質閘門]
P3[安全掃描]
end
subgraph "工具層 - Tools"
T1[GitHub Copilot]
T2[SonarQube]
T3[OWASP Dep-Check]
T4[MCP Servers]
T5[Custom Agents]
end
G1 --> P1
G2 --> P2
G3 --> P3
P1 --> T1
P2 --> T2
P3 --> T3
P1 --> T4
P1 --> T5
style G1 fill:#3b82f6,color:#fff
style G2 fill:#3b82f6,color:#fff
style G3 fill:#3b82f6,color:#fff
style T4 fill:#f59e0b,color:#fff
style T5 fill:#f59e0b,color:#fff治理原則
| 原則 | 說明 |
|---|---|
| 人類負責制 | AI 生成的程式碼,由提交者負全責 |
| 可追溯性 | 標記 AI 參與的 Commit(如 Commit Message 加上 [AI-Assisted]) |
| 資料保護 | 禁止將客戶資料、生產環境資料輸入 AI |
| 品質標準 | AI 生成的程式碼與人工程式碼遵循相同品質標準 |
| 持續教育 | 定期培訓團隊正確使用 AI 工具 |
| Agent 管控 | Cloud/Background Agent 產出的程式碼必須經過人工 Review |
| MCP 存取控制 | MCP Server 存取的外部系統需經資安團隊核可 |
| Agent 權限分級 | 依場景限制 Agent 可用工具(如禁止 Agent 存取生產 DB) |
| 成本管控 | 監控 Session 級別 Credits 消耗,設定團隊月度預算上限 |
| Session 審計 | 啟用 Session Sync,保留所有 AI 對話歷史作為審計紀錄 |
| Autopilot 限制 | 機敏環境禁用 Autopilot,使用 Ask 權限層級 |
| 企業 MDM 管理 | 透過 MDM 統一部署 Copilot 政策設定,開發者無法本地覆寫 |
🏦 企業實務建議:
- 每月舉辦「AI 輔助開發分享會」,分享好的 Prompt 和使用技巧
- 建立團隊共用的 Prompt Library(
.github/prompts/目錄)- 在 Jira Story 中增加「AI 使用比例」欄位,追蹤 AI 參與度
- 建立 MCP Server 白名單制度,由資安團隊定期審查
- 定義 Custom Agent 的工具權限控制標準
- 啟用 Session Sync + Chronicle,利用
/chronicle指令產出團隊生產力報告- 透過 MDM 統一部署
chat.permissions.default、chat.tools.global.autoApprove等政策- 設定
extensions.autoUpdateDelay為 24–72 小時,確保穩定性- 監控團隊 Copilot Credits 消耗,設定 Additional Spend 上限
11. 常見問題與最佳解法(FAQ)
Q1:Copilot 產生錯誤程式碼怎麼辦?
A:
- 不要盲目接受 — 理解每行程式碼的邏輯
- 提供更多上下文 — 使用
#file引用相關檔案 - 使用 Chat 修正 — 選取錯誤程式碼,使用
/fix - 切換模型 — 複雜邏輯嘗試 Claude Sonnet 4 或 Opus
Q2:VS Code Java 效能問題(卡頓)?
A:
// settings.json
{
"java.jdt.ls.vmargs": "-Xmx2g -Xms512m",
"java.import.exclusions": [
"**/node_modules/**",
"**/.metadata/**",
"**/archetype-resources/**"
],
"files.watcherExclude": {
"**/target/**": true,
"**/.git/objects/**": true
}
}Q3:Maven 依賴下載失敗?
A:
- 檢查 Proxy 設定(
~/.m2/settings.xml) - 確認企業 Nexus Repository 位址
- 刪除損壞的依賴:
mvn dependency:purge-local-repository - 強制更新:
mvn clean install -U
Q4:Spring Boot 啟動後 API 回傳 401?
A:
- 確認 SecurityConfig 是否有放行必要端點
- 開發階段可暫時放行所有端點:
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())Q5:Copilot 建議太慢或沒反應?
A:
- 檢查網路連線(尤其企業 Proxy)
- 確認 Copilot 訂閱狀態
- 重新登入 GitHub
- 重啟 VS Code(
Ctrl + Shift + P→Reload Window)
Q6:如何讓 Copilot 更了解我的專案?
A:
- 建立
.github/copilot-instructions.md - 使用
@workspace指令引用整個工作區 - 使用
#file引用關鍵檔案 - 在 Chat 中提供架構說明
Q7:JUnit 測試在 VS Code 中無法執行?
A:
- 確認 Java Test Runner Extension 已安裝
- 檢查
pom.xml中spring-boot-starter-test依賴 - 確認測試類在
src/test/java目錄下 - 重建 Java Language Server:
Ctrl + Shift + P→Java: Clean Language Server Workspace
Q8:如何同時開發多個微服務?
A:
- 使用 VS Code Multi-root Workspace:
// workspace.code-workspace
{
"folders": [
{ "path": "../banking-api" },
{ "path": "../customer-api" },
{ "path": "../notification-service" }
]
}Q9:Lombok 不生效(getter/setter 找不到)?
A:
- 確認
pom.xml包含 Lombok 依賴 - 安裝 Lombok Annotations Support Extension
- VS Code 重新載入:
Java: Clean Language Server Workspace
Q10:如何避免 Copilot 產生過時的 API?
A:
- 在
copilot-instructions.md中明確指定版本:
- Java 版本:21
- Spring Boot 版本:3.4.x
- 禁止使用 javax.* 套件,使用 jakarta.*
- 使用 SecurityFilterChain 取代 WebSecurityConfigurerAdapter- 使用
#file:pom.xml讓 Copilot 參考實際依賴版本
Q11:如何處理 Copilot 在企業 Proxy 後面無法使用?
A:
// settings.json
{
"http.proxy": "http://proxy.company.com:8080",
"http.proxyStrictSSL": false,
"github.copilot.advanced": {
"authProvider": "github"
}
}同時確認防火牆已開放:
github.comapi.github.comcopilot-proxy.githubusercontent.com
Q12:Hot Reload(熱載入)如何設定?
A:
<!-- pom.xml 加入 devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>// settings.json
{
"java.autobuild.enabled": true
}Q13:Agent Mode 執行時異常終止怎麼辦?
A:
- 檢查 Agent Debug Log:
Ctrl + Shift + P→Chat: Show Agent Debug Logs - 確認終端權限——Agent 可能因權限不足無法執行終端指令
- 檢查工作區磁碟空間——Agent 可能因空間不足而失敗
- 嘗試縮小任務範圍,分步驟執行
- 切換模型後重試(某些模型對 Agent 任務表現更佳)
Q14:MCP Server 連線失敗?
A:
- 確認 MCP Server 運作指令已安裝(如
npx需要 Node.js) - 檢查
settings.json中的 MCP 設定是否正確 - 確認環境變數已正確設定(Token、URL 等)
- 防火牆/Proxy 可能阻擋 MCP Server 對外連線
Ctrl + Shift + P→MCP: List Servers檢查狀態
Q15:Edit Mode 去哪了?
A:
- Edit Mode 已在 VS Code 1.125 完全移除
- 替代方案:使用 Agent Mode 處理多檔案編輯任務
- Agent Mode 功能遠超 Edit Mode,支援自主迭代、終端操作、檔案建立
- 搭配 Autopilot 權限層級可實現全自動多步驟編輯
Q16:Autopilot 會不會失控修改程式碼?
A:
- Autopilot 最多迭代 三次後自動停止
- 任務目標以 Tooltip 顯示在 Chat 上方,隨時可檢視
- 可透過
chat.permissions.default設定預設權限層級 - 企業環境可用
chat.tools.global.autoApprove政策完全禁用 - 建議機敏環境使用 Ask 權限,一般開發用 Auto-approve
Q17:Agents Window 如何跨機器同步 Session?
A:
- 啟用
chat.sessionSync.enabled設定 - 確認已登入 GitHub 帳號
- Session 自動同步至 GitHub,包含對話、檔案清單、Repository 上下文
- 在任何裝置的 VS Code 中可搜尋和恢復歷史 Session
- 使用
/chronicle search搜尋歷史工作內容
Q18:如何控制 Copilot 使用成本?
A:
- 監控成本:Session 資訊彈出視窗查看 Credits 消耗
- Additional Spend 上限:在 GitHub Settings > Copilot 設定每月額外預算
- 選擇合適模型:日常用 GPT-4o(低成本),只在需要時切換高階模型
- 調低 Context Size:非必要不使用 Extended (1M) Context
- 降低 Thinking Effort:簡單任務用 Low/Medium
- 控制 Agent 迭代:使用 Ask 權限減少不必要的自動迭代
Q19:如何設定企業級 MCP 認證?
A:
- IT 管理員透過 MDM 部署
mcp.enterpriseManagedAuth.idp政策 - 在
mcp.json的 MCP Server 設定中加入"oauth": { "enterpriseManaged": true } - 使用者只需登入一次企業 IdP(Entra/Okta/Auth0)
- VS Code 自動走 XAA 流程為每個 MCP Server 取得 Token
- 參考 xaa.dev 了解 ID-JAG 標準
Q20:Research Agent 與一般 Chat 有什麼差別?
A:
- Research Agent 是唯讀的,不會修改任何程式碼
- 輸出是結構化 Markdown 報告,含引用來源
- 搜尋範圍包含 Codebase + GitHub Repos + Web
- 適合技術調研、方案比較、新技術學習
- 使用方式:在 Copilot CLI Session 中輸入
/research <主題>
12. 附錄
12.1 常用指令速查表
VS Code 快捷鍵
| 快捷鍵 | 功能 |
|---|---|
Ctrl + Shift + P | 指令面板 |
Ctrl + P | 快速開啟檔案 |
Ctrl + Shift + F | 全域搜尋 |
Ctrl + Shift + H | 全域替換 |
F2 | 重新命名符號 |
F12 | 前往定義 |
Alt + F12 | Peek 定義 |
Ctrl + . | 快速修復 / 建議 |
Ctrl + Space | 觸發 IntelliSense |
Ctrl + / | 切換註解 |
Alt + ↑/↓ | 移動行 |
Ctrl + Shift + K | 刪除行 |
Ctrl + `` | 開啟/關閉終端 |
Maven 常用指令
| 指令 | 說明 |
|---|---|
mvn clean | 清除 target 目錄 |
mvn compile | 編譯 |
mvn test | 執行測試 |
mvn package | 打包 JAR |
mvn install | 安裝到本地 Repository |
mvn spring-boot:run | 啟動 Spring Boot |
mvn dependency:tree | 顯示依賴樹 |
mvn versions:display-dependency-updates | 檢查依賴更新 |
mvn clean install -DskipTests | 跳過測試打包 |
mvn test -Dtest=AccountServiceTest | 執行指定測試類 |
Git 常用指令
| 指令 | 說明 |
|---|---|
git checkout -b feature/xxx | 建立並切換分支 |
git add . | 暫存所有變更 |
git commit -m "feat: xxx" | 提交 |
git push origin feature/xxx | 推送分支 |
git pull --rebase origin develop | 拉取並 Rebase |
git stash / git stash pop | 暫存/還原工作區 |
git log --oneline -10 | 查看最近 10 筆 Commit |
git diff --stat develop | 與 develop 比較差異 |
12.2 範例 Prompt 清單
Entity / DTO 生成
| # | Prompt |
|---|---|
| 1 | 建立 Customer Entity,包含 id、name、email、phone、idNumber(身分證字號)、createdAt、updatedAt,使用 JPA + Lombok |
| 2 | 根據 #file:Customer.java 建立 CustomerRequest DTO,使用 Java Record + Jakarta Validation |
| 3 | 建立 PageResponse<T> 泛型分頁回應 DTO,包含 content、totalElements、totalPages、currentPage |
Controller 生成
| # | Prompt |
|---|---|
| 4 | 建立 CustomerController,路徑 /api/v1/customers,包含 CRUD + 分頁查詢,回傳 ApiResponse 統一格式 |
| 5 | 在 AccountController 中新增 PUT /api/v1/accounts/{id}/freeze 凍結帳戶端點 |
| 6 | 建立 GlobalExceptionHandler,處理 BusinessException、MethodArgumentNotValidException、通用 Exception |
Service 生成
| # | Prompt |
|---|---|
| 7 | 參考 #file:AccountServiceImpl.java 的風格,建立 CustomerServiceImpl,包含 CRUD 和搜尋功能 |
| 8 | 實作轉帳服務,需包含:餘額檢查、帳戶狀態驗證、交易紀錄寫入,使用 @Transactional |
| 9 | 建立排程任務 ScheduledTaskService,每日凌晨 2 點執行利息計算,使用 @Scheduled |
Repository 生成
| # | Prompt |
|---|---|
| 10 | 建立 TransactionRepository,包含:依帳號查詢、依日期範圍查詢、依交易類型統計金額,使用 Spring Data JPA |
| 11 | 為 AccountRepository 新增自訂 JPQL 查詢:依餘額範圍和帳戶類型搜尋 |
測試生成
| # | Prompt |
|---|---|
| 12 | /tests 為 #file:TransferMoneyUseCase.java 生成完整測試,使用 JUnit 5 + Mockito |
| 13 | 建立 AccountController 的 MockMvc 整合測試,涵蓋 200、400、401、404 狀態碼 |
| 14 | 為 Money Value Object 建立測試,涵蓋加減乘除、幣別檢查、邊界值 |
設定 / 組態
| # | Prompt |
|---|---|
| 15 | 建立 Spring Security 設定,使用 JWT 認證、停用 Session、放行 /api/v1/auth/** 和 actuator |
| 16 | 產生 application.yml,包含多環境設定(dev/sit/uat/prod),含 DataSource、JPA、Logging |
| 17 | 建立 CORS 設定,允許前端 http://localhost:3000 存取 |
CI/CD / DevOps
| # | Prompt |
|---|---|
| 18 | 產生 GitHub Actions CI workflow,包含 Build、Test、JaCoCo、SonarQube、OWASP Dependency-Check |
| 19 | 建立 Dockerfile(多階段建置),使用 Eclipse Temurin JRE 21 Alpine,暴露 8080 port |
| 20 | 產生 docker-compose.yml,包含 Spring Boot App + PostgreSQL + Redis |
重構 / 優化
| # | Prompt |
|---|---|
| 21 | 將此 if-else 鏈重構為 Strategy Pattern |
| 22 | 將這段程式碼的 Optional 處理改用 Java 21 Pattern Matching |
| 23 | @workspace 找出所有直接回傳 Entity 的 Controller,建議改為 DTO |
13. 檢查清單(Checklist)
新成員快速上手 Checklist
環境設定
- 安裝 VS Code(1.126+ 最新穩定版)
- 安裝 JDK 21(設定 JAVA_HOME)
- 安裝 Maven 3.9+(設定 MAVEN_HOME)
- 安裝 Git 2.40+
- 安裝必備 VS Code Extension(見 2.2 章節)
- 設定 VS Code settings.json(見 2.3 章節)
- 登入 GitHub 並啟用 Copilot
- 執行
/init建立專案 Copilot 設定(見 2.4 章節) - 啟用 Session Sync(
chat.sessionSync.enabled) - 設定企業 Proxy(如需要)
- 設定 Maven settings.xml(企業 Nexus)
- Clone 專案並成功
mvn compile
開發流程
- 了解專案架構(Clean Architecture 分層)
- 閱讀
copilot-instructions.md - 了解分支策略(Git Flow)
- 了解 Commit Message 規範
- 能使用 Copilot Chat 生成程式碼
- 能使用 Agent Mode 執行多步驟任務
- 了解 Autopilot 權限層級與適用場景
- 能使用 Agents Window 管理多個 Session
- 能使用
/chronicle搜尋歷史工作 - 能使用 REST Client 測試 API
- 能使用 VS Code Debug 除錯
- 能撰寫 JUnit 5 測試
- 能使用整合式瀏覽器測試 Web 應用(含截圖、收藏)
安全與品質
- 了解 Copilot 使用規範(不輸入敏感資料)
- 了解 AI 生成程式碼的安全檢查清單
- 了解 Code Review 流程
- 了解 SonarQube 品質標準
- 了解 OWASP Top 10 常見弱點
- 了解 Agent / MCP 使用的安全與治理規範
- 了解 Permission Level 設定(Ask / Auto-approve / Autopilot)
- 了解 Session 成本追蹤與預算管理
團隊協作
- 加入專案 GitHub Repository
- 了解 PR 提交流程(含 Cloud Agent PR Review)
- 了解 CI/CD Pipeline 運作方式
- 參加 AI 輔助開發分享會(每月)
- 熟悉 Chat Customizations Editor 管理自訂化設定
- 了解 Session Sync 與 Chronicle 指令使用方式
- 了解 MCP Server 白名單與企業認證機制
📌 文件維護:本手冊由技術團隊維護,建議每季更新一次,確保內容與最新版本的 VS Code、Copilot、Spring Boot 同步。
📞 聯絡方式:如有問題,請聯繫技術架構團隊或在 Teams 頻道中提問。
文件結束 — VS Code + GitHub Copilot 開發 Java Web 應用程式教學手冊 v3.0(根據 VS Code 1.123–1.126 全面更新)