Spring Boot – Gradle 多模組架構

本文件透過商業智慧研究中心 – 後台系統來說明商智中心的Spring Boot架構

版本:0.1.0
最後更新時間:06/10/2026
最後更新人員:陳泓毓


1. 專案概覽

項目說明
框架Spring Boot 2.6.6
語言Java 11
建置工具Gradle(多模組專案)
資料庫MySQL 8

本專案為 RESTful API 後端,供前端管理介面(React 等 SPA)呼叫

2. 系統架構

2.1 整體拓撲

┌─────────────────┐       HTTPS/HTTP        ┌──────────────────────────────┐
│   前端 SPA       │  ──────────────────────▶ │  birc-management-backend     │
│ (React 等)       │  Authorization: Bearer   │  Spring Boot (port 8080)     │
│                 │  ◀────────────────────── │                              │
└─────────────────┘       JSON 回應          └──────────┬───────────────────┘
                                                         │
                              ┌──────────────────────────┼──────────────────────┐
                              │                          │                      │
                              ▼                          ▼                      ▼
                     ┌────────────────┐        ┌─────────────────┐    ┌─────────────────┐
                     │  MySQL 資料庫   │        │  本機檔案系統     │    │  內部 Maven Repo  │
                     │  schema: events │        │  image/ file 目錄 │    │  (共用函式庫)     │
                     └────────────────┘        └─────────────────┘    └─────────────────┘Code language: JavaScript (javascript)

2.2 職責劃分

層級職責
前端UI 呈現、表單驗證、攜帶 JWT Token 發送 API 請求
Controller接收 HTTP 請求、參數驗證、組裝 JSON 回應
Service商業邏輯、交易控制、Entity ↔ Bean 轉換協調
DAO(Repository)資料庫 CRUD,基於 Spring Data JPA
Entity對應資料庫資料表結構(JPA 映射)
BeanAPI 傳輸物件,用於接收前端輸入或回傳資料
TransformerEntity 與 Bean 之間的轉換邏輯

2.3 設計原則

  • Entity 不直接暴露給前端:Controller 透過 Bean 或手動組裝 ObjectData 回傳,避免洩漏密碼等敏感欄位。
  • 讀寫分離的 Service 基底類別:唯讀操作繼承 BaseViewServiceImpl,含寫入操作繼承 BaseServiceImpl
  • 設定與資料庫映射分離為獨立 Gradle 子模組,方便維護與重用。

3. Gradle 多模組結構

birc-management/                          ← 根專案(主應用程式)
├── build.gradle
├── settings.gradle
├── src/main/java/.../management/         ← 業務邏輯主程式
│   ├── BircManagementApplication.java    ← 啟動類別
│   ├── controller/
│   ├── service/
│   ├── bean/
│   ├── dto/
│   ├── exception/
│   ├── schedule/
│   └── util/
├── src/main/resources/
│   ├── application.yml
│   └── application-local.yml             ← 本地設定(需自行建立,不進版控)
└── modules/
    ├── birc-management-config/           ← 安全、Swagger、靜態資源等設定
    │   └── config/
    │       ├── SecurityConfig.java
    │       ├── filter/                   ← JWT、登入 Filter
    │       ├── handler/                  ← 登入成功/失敗處理
    │       └── properties/               ← 設定檔屬性綁定
    └── birc-management-database-config/  ← 資料庫層
        └── databaseconfig/
            ├── entity/                   ← JPA Entity
            ├── dao/                      ← Spring Data JPA Repository
            ├── dto/                      ← 分頁等資料傳輸物件
            └── entity/listener/          ← JPA 生命週期監聽器Code language: PHP (php)

模組依賴關係

birc-management (主模組)
    ├── modules:birc-management-config
    ├── modules:birc-management-database-config
    └── tw.edu.ntub.birc:common:1.1.0   ← 校內共用函式庫Code language: CSS (css)

4. Spring Boot 分層架構

4.1 啟動類別

BircManagementApplication 是整個應用程式的進入點:

@EnableScheduling
@SpringBootApplication(exclude = {UserDetailsServiceAutoConfiguration.class})
public class BircManagementApplication extends SpringBootServletInitializer { ... }Code language: JavaScript (javascript)

重點說明:

  • @EnableScheduling:啟用排程任務(如自動簽退)。
  • 排除 UserDetailsServiceAutoConfiguration:使用自訂的 UserDetailsServiceImpl
  • 繼承 SpringBootServletInitializer:支援打包成 WAR 部署至外部 Tomcat(目前 war { enabled = false },以 JAR 方式執行)。

4.2 各層職責與命名慣例

套件命名慣例說明
controllerXxxController@RestController + @RequestMapping,處理 HTTP
serviceXxxService(介面)、XxxServiceImpl(實作)@Service,商業邏輯
service.transformerXxxTransformerXxxTransformerImplEntity ↔ Bean 轉換
beanXxxBean前端傳入/傳出的資料物件,可加 @Valid 驗證
databaseconfig.entity與資料表同名JPA @Entity
databaseconfig.daoXxxDAO繼承 BaseDAOBaseViewDAO
exceptionXxxException自訂商業例外
scheduleXxxSchedule@Component + @Scheduled

4.3 Service 繼承體系

BaseViewService<B, ID>           ← 唯讀介面(get、search)
    └── BaseService<B, ID>       ← 加入 update、delete

BaseViewServiceImpl<B, E, ID>    ← 唯讀實作(泛型:Bean, Entity, 主鍵型別)
    └── BaseServiceImpl<B, E, ID>← 加入寫入實作Code language: HTML, XML (xml)

泛型參數說明:

  • B:Bean(API 層物件)
  • E:Entity(資料庫物件)
  • ID:主鍵型別(如 StringInteger

範例:UserServiceImpl extends BaseServiceImpl<UserBean, User, String>

4.4 Transformer 模式

BeanEntityTransformer<B, E> 定義雙向轉換:

E transferToEntity(B b);   // Bean → Entity(新增/更新時)
B transferToBean(E e);     // Entity → Bean(查詢回傳時)Code language: JavaScript (javascript)

每個業務領域有獨立的 Transformer 實作(如 UserTransformerImpl),由 Spring 自動注入至對應的 Service。


5. 請求處理流程

GET /user/{account} 為例:

HTTP Request
    │
    ▼
JwtAuthenticationFilter          ← 解析 Authorization: Bearer <token>
    │                              設定 SecurityContextUserController.getUser()       ← @GetMapping,呼叫 Service
    │
    ▼
UserServiceImpl.getByAccount() ← 商業邏輯
    │
    ▼
UserDAO.findByAccount()        ← Spring Data JPA 查詢
    │
    ▼
MySQL (events.user 資料表)
    │
    ▼ (回傳路徑)
User Entity → 手動組裝 ObjectData → ResponseEntityBuilder → JSON 字串Code language: CSS (css)

Controller 撰寫慣例

  • 使用 @AllArgsConstructor(Lombok)進行建構子注入,不使用 @Autowired 欄位注入。
  • 回傳型別為 ResponseEntity<String>(JSON 字串),透過 ResponseEntityBuilder 統一格式。
  • 需要權限的端點加上 @PreAuthorize("hasAnyAuthority('Manager')")
  • 表單驗證使用 @Valid + BindingResultUtils.validate(bindingResult)

6. 安全與認證(Spring Security + JWT)

6.1 認證流程

1. 前端 POST /login(JSON 或 form-data)
       account + password
           │
           ▼
2. CustomLoginFilter 攔截登入請求
           │
           ▼
3. CustomAuthenticationProvider 驗證帳密
           │
           ▼
4. CustomAuthenticationSuccessHandler 產生 JWT
           │
           ▼
5. 回應 Header: X-Auth-Token: <jwt>

6. 後續請求帶入 Authorization: Bearer <jwt>
           │
           ▼
7. JwtAuthenticationFilter 解析 Token,設定 SecurityContextCode language: HTML, XML (xml)

6.2 權限角色

權限透過 UserRole 資料表管理,常見角色:

Authority說明
Manager管理員,可存取多數管理功能
(其他角色依資料庫設定)一般成員等

使用 @PreAuthorize 在方法層級控制存取:

@PreAuthorize("hasAnyAuthority('Manager')")
@GetMapping(path = "")
public ResponseEntity<String> searchUsers() { ... }Code language: JavaScript (javascript)

6.3 公開端點

以下路徑不需認證(定義於 SecurityConfig):

  • Swagger 文件:/api/v3/**/swagger-ui/**
  • 靜態資源:/static/**/webjars/**
  • 圖片/檔案:/image/**/file/**(依設定)

6.4 Session 策略

設定為 SessionCreationPolicy.STATELESS,不依賴伺服器端 Session,完全以 JWT 進行無狀態認證。


7. 資料存取層(JPA)

7.1 Entity

Entity 位於 modules/birc-management-database-config/.../entity/,使用標準 JPA 註解:

@Table(name = "user", schema = Config.DATABASE_NAME)  // schema = "events"
@Entity
@Data
@EntityListeners(UserListener.class)
public class User {
    @Id
    @Column(name = "account")
    private String account;

    @ManyToOne
    @JoinColumn(name = "user_role", referencedColumnName = "role_id")
    private UserRole userRole;
    // ...
}Code language: PHP (php)

注意事項:

  • 資料庫 schema 名稱統一定義在 Config.DATABASE_NAME = "events"
  • ddl-auto: none不自動建表,資料表結構由 DBA 或 migration 腳本維護。
  • 部分 Entity 有 @EntityListeners,在 persist/update 前後執行自訂邏輯(如自動填入時間戳)。

7.2 DAO(Repository)

public interface UserDAO extends BaseDAO<User, String> {
    Optional<User> findByAccount(String account);
    String findChineseNameByAccount(String account);
}Code language: PHP (php)
  • BaseDAO 繼承 JpaRepository,提供標準 CRUD。
  • BaseViewDAO 僅提供唯讀操作,用於 View 或統計查詢。
  • 自訂查詢方法依 Spring Data JPA 命名規則(findByXxx)或 @Query 撰寫。

7.3 交易管理

交易透過兩種機制控制:

  1. AOP 攔截器databaseconfig.Config):依方法名稱前綴自動套用交易屬性。
  • save*update*delete*create* → 需要交易(REQUIRED
  • get*search*find* → 唯讀(NOT_SUPPORTED
  1. @Transactional 註解:在 Service 方法上明確標示(如 UserServiceImpl.updatePassword)。

主應用程式中另有 AOP 設定,對 BaseViewService 套件下的所有方法套用 transactionInterceptor


8. API 回應格式與例外處理

8.1 統一回應格式

所有 API 回傳統一的 JSON 結構(由 ResponseEntityBuilder 產生):

{
  "result": true,
  "errorCode": "",
  "message": "查詢成功",
  "data": { ... }
}Code language: JavaScript (javascript)

失敗時 resultfalseerrorCodemessage 說明錯誤原因。

8.2 例外處理

ExceptionHandleController@ControllerAdvice)集中處理所有例外:

例外類型處理方式
ProjectException(含子類別)回傳對應 errorCode 與 message
AccessDeniedException權限不足提示
BindException表單驗證錯誤訊息
MaxUploadSizeExceededException檔案過大
Exception(兜底)包裝為 UnknownException

自訂商業例外應繼承 ProjectException(來自 tw.edu.ntub.birc.common),以確保被統一處理。


9. 檔案上傳與靜態資源

9.1 上傳流程

  1. Controller 接收 MultipartFile(通常透過 Bean 的 List<MultipartFile> images 欄位)。
  2. 呼叫 UploadFileService.uploadFile() 儲存至本機目錄。
  3. 檔案資訊寫入 upload_file 資料表。
  4. 透過靜態資源路徑對外提供存取。

9.2 靜態資源映射

birc-management-configConfig.java 將 URL 路徑映射至本機目錄:

URL 路徑本機目錄(由 application-local.yml 設定)
/image/**server.image.path
/file/**server.file.path

9.3 檔案大小限制

application.yml 設定:

spring.servlet.multipart:
  max-file-size: 200MB
  max-request-size: 250MBCode language: CSS (css)

10. 排程任務

排程類別位於 src/.../schedule/,以 @Component + @Scheduled 實作:

類別Cron 表達式功能
ClockInSchedule0 0 3 * * *(每日凌晨 3 點)自動簽退未打卡離開的紀錄
OnDutySchedule0 0 0 * * *(每日午夜)值班相關自動作業

時區統一使用 Asia/Taipei


11. 開發環境設定

11.1 必要條件

  • Java 11(OpenJDK 11.0.2 或以上)
  • Gradle(或使用專案內建的 Gradle Wrapper)
  • MySQL 8 資料庫(schema: events

11.2 本地設定檔

  1. 複製 src/main/resources/application-local-example.yml
  2. 重新命名為 application-local.yml
  3. 修改以下項目:
server:
  path: http://localhost:8080
  image:
    path: 'D:\birc-management\UploadFile\image\'
  file:
    path: 'D:\birc-management\UploadFile\file\'

birc-management:
  database:
    url: jdbc:log4jdbc:mysql://localhost:3306/events?serverTimezone=Asia/Taipei
    account: 你的帳號
    password: 你的密碼Code language: PHP (php)

application-local.yml 含敏感資訊,不應提交至版控

11.3 啟動應用程式

# Windows
gradlew bootRun

# 或直接執行 main
# BircManagementApplication.main()Code language: PHP (php)

啟動後:

  • API 服務:http://localhost:8080
  • Swagger UI:http://localhost:8080/api

11.4 日誌

使用 Log4j2(排除預設的 Logback),設定檔為 src/main/resources/log4j2.yml
SQL 偵錯可透過 log4jdbc 在 application-local.yml 開啟 sqltiming: DEBUG


12. 新增功能開發指南

假設要新增「公告(Announcement)」功能,建議依以下順序開發:

Step 1:建立 Entity 與 DAO

modules/birc-management-database-config 中:

entity/Announcement.java      ← 對應資料表
dao/AnnouncementDAO.java      ← extends BaseDAO<Announcement, Integer>Code language: HTML, XML (xml)

Step 2:建立 Bean 與 Transformer

在主模組 src/main/java/.../management 中:

bean/AnnouncementBean.java
service/transformer/AnnouncementTransformer.java
service/transformer/impl/AnnouncementTransformerImpl.java

Step 3:建立 Service

service/AnnouncementService.java          ← 介面
service/impl/AnnouncementServiceImpl.java ← extends BaseServiceImpl<...>Code language: HTML, XML (xml)

Step 4:建立 Controller

controller/AnnouncementController.java
@RestController
@AllArgsConstructor
@RequestMapping(path = "announcement")
public class AnnouncementController {
    private final AnnouncementService announcementService;

    @GetMapping
    public ResponseEntity<String> searchAll() { ... }
}Code language: PHP (php)

Step 5:視需要加入權限與例外

  • 管理端點加上 @PreAuthorize
  • 自訂例外繼承 ProjectException

開發檢查清單

  • [ ] Entity 欄位與資料表一致,schema = Config.DATABASE_NAME
  • [ ] DAO 方法命名符合 Spring Data JPA 規則
  • [ ] Service 寫入方法有 @Transactional 或由 AOP 攔截器涵蓋
  • [ ] Controller 使用 ResponseEntityBuilder 回傳統一格式
  • [ ] 敏感操作有 @PreAuthorize 保護
  • [ ] 表單輸入有 @Valid 驗證

13. 常用套件與外部依賴

套件用途
spring-boot-starter-webREST API、內嵌 Tomcat
spring-boot-starter-security認證與授權
spring-boot-starter-data-jpaORM 與 Repository
spring-boot-starter-validationBean Validation(@Valid@NotNull 等)
springdoc-openapi-uiSwagger / OpenAPI 文件
lombok減少樣板程式碼(@Data@AllArgsConstructor
log4j2 + log4jdbc日誌與 SQL 偵錯
tw.edu.ntub.birc:common校內共用工具(JavaBeanUtilsCollectionUtils 等)
commons-beanutilsBean 屬性複製
jacksonJSON 序列化/反序列化