Spring TransactionTemplate 程式化交易管理與 Rollback
當初一開始用 Spring,習慣只會 @Transactional 這招。後來才發現 TransactionTemplate 這東西,發現它在某些場景真的有夠方便。 @Transactional 的限制@Transactional 很直觀啦,加一個 annotation 就搞定交易管理。但踩坑的地方是: Proxy 限制 - 同個 class 內部呼叫不會觸發 proxy,交易會爆掉 粒度問題 - 整個方法才是一個交易,沒辦法在同一個方法裡控制多段獨立的交易 Rollback 控制 - 用 rollbackFor 有點硬,不能細粒度決定要不要 rollback TransactionTemplate 的救世主角色TransactionTemplate 提供了「程式化」的交易控制。不用 annotation,直接寫程式邏輯,搭配 callback 來執行。 基本用法首先注入 PlatformTransactionManager: 12345678910111213141516171819202122232425262728@Servicepublic class OrderSe...
Spring @Transactional 類別層級效果與 CGLIB Proxy
@Transactional 是 Spring 最常用的註解之一,但很多人對它的底層原理不太了解,導致寫出一些表面上沒問題,但實際上交易沒有生效的程式碼。我就踩過好幾次這種坑,最後才徹底理解它的運作方式。 類別層級 vs 方法層級@Transactional 可以標在類別上,也可以標在方法上。 標在類別上1234567891011121314151617@Service@Transactionalpublic class UserService { public void updateUser(User user) { // 有交易 } public void deleteUser(String userId) { // 也有交易 } @Transactional(readOnly = true) public User getUser(String userId) { // 覆蓋了類別層級的設定,用 readOnly ...
Spring Batch Partitioner 分區處理流程
在處理大量資料時,如果單線程處理會很慢。Spring Batch 的 Partitioner 就是用來把大任務分成多個小區塊,然後平行處理。我之前有個 ETL job 處理 1000 萬筆資料,用了 Partitioner 之後,執行時間直接從 2 小時降到 15 分鐘。 Partitioner 的核心概念Partitioner 做的事很簡單:把一個大任務分成 N 個小任務。 假設你要處理 1000 萬筆資料: 沒有 Partitioner:一個線程處理全部,花 1 小時 有 Partitioner(8 個 partition):8 個線程各處理 125 萬筆,花 7.5 分鐘 核心元件1. Partitioner InterfacePartitioner 的工作就是定義怎麼分割。你要實現 Partitioner interface: 12345678910111213141516171819202122232425262728public class CustomPartitioner implements Partitioner { @Overri...
Spring Boot REST API 檔案下載實作
檔案下載功能在 web 應用裡很常見,但實作得不夠好的話,會導致記憶體爆炸。我之前有個專案就因為沒注意這點,結果下載一個 1GB 的檔案直接把 Java heap 炸了。 今天就來介紹怎麼用 Spring Boot 安全地實作檔案下載,尤其是大檔案。 問題:傳統做法為什麼不行?傳統的做法可能是這樣的: 1234567891011121314@GetMapping("/download/{fileId}")public ResponseEntity<byte[]> downloadFile(@PathVariable String fileId) { byte[] fileContent = fileService.getFileContent(fileId); // 整個檔案都讀進記憶體! HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); ...
Spring MVC Redirect 傳遞參數完整指南
在 Spring MVC 裡面做 redirect 再常見不過,但怎麼安全地傳遞參數,就有點講究了。之前有個專案因為 redirect 時沒處理好敏感資訊,差點出事。今天就完整介紹幾種傳遞參數的方式。 問題背景假設用戶提交了一個表單,你的 controller 處理完後想要 redirect 到另一個頁面。但有時候你需要傳一些資料給下一個頁面,比如成功/失敗的訊息,或者使用者 ID。 最簡單的方式就是加到 URL query string: 1redirect:/order/confirm?orderId=12345&status=success 但這樣有問題: 敏感資訊會暴露在 URL 上 用戶可以直接改 URL query string 作弊 如果參數很多,URL 會很長很醜 更好的方式是用 RedirectAttributes。 RedirectAttributes 的兩種方式方式一:addAttribute(加到 URL query string)1234567@PostMapping("/order/create")publi...
Spring Cloud Config Server 本地檔案模式設定
Spring Cloud Config 是用來集中管理應用設定的利器,但預設用 Git backend,在開發時有時候有點麻煩。我就來介紹怎麼用本地檔案模式搞定開發環境的設定管理。 為什麼要用本地檔案模式?預設的 Config Server 是用 Git repository 當後端,這對生產環境來說很棒,但開發時有點費事: 要維護一個 Git repo 改設定要 push、pull 不方便快速迭代 用本地檔案模式就簡單多了,直接在電腦上改檔案,重新整理一下就行。 Config Server 的設定首先建立一個 Spring Boot application 當 Config Server。 1. 加依賴1234<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId></dependency> 2. 啟動類別標注 @EnableCo...
Spring Cloud Config Client 設定與 @ConfigurationProperties
上一篇文章講了 Config Server 的設定,這篇就來講怎麼在 client 端正確地拉和使用這些設定。有不少人在這邊踩過坑,主要都是因為搞不清楚 bootstrap.yml 和 application.yml 的區別。 bootstrap.yml vs application.yml這是最關鍵的一點,千萬別搞反了。 bootstrap.yml - 用來設定 Spring Cloud 相關的東西,比如 Config Server 的位置。它的載入優先序比 application.yml 更高 application.yml - 普通的應用設定 如果你把 Config Server 的設定寫在 application.yml,Spring 會先載入 application.yml,但這時候還沒有連到 Config Server,所以設定會不全。一定要寫在 bootstrap.yml。 Config Client 的基本設定在你的 Spring Boot 應用裡,先加依賴: 1234<dependency> <groupId>org.sprin...
ObjectMapper 泛型 JSON 序列化與反序列化
在 Java 裡面用 ObjectMapper(Jackson)處理 JSON 再正常不過了,但一旦涉及到泛型,事情就開始變得有點複雜。我之前在這邊踩過好幾次坑,最後發現居然是反序列化出來的不是我預期的物件。 為什麼泛型會出問題?先說為什麼會有這個問題。Java 的泛型是編譯時的特性,在 runtime 時會被擦除。也就是說,List<MyObj> 和 List<String> 在 runtime 時看起來都是一樣的 List,沒辦法區分。 所以當你這樣寫的時候: 12String json = "[{\"name\": \"Alice\"}, {\"name\": \"Bob\"}]";List<MyObj> list = objectMapper.readValue(json, List.class); Jackson 不知道你想要 List<MyObj>,反序列化出來的就是 List&l...
Spring MVC AOP 設定與 AspectJ 用法
AOP(面向切面編程)這個概念一開始聽起來很唬爛,但其實就是把某些跨越多個業務邏輯的共同功能(比如 logging、權限驗證、效能監控)提取出來,用一個統一的地方來管理。 我之前有個專案就是沒用 AOP,logging 的程式碼散落在各個 service 裡面,後來改用 AOP 重構了一遍,爽度直接升天。 前置準備首先要加上依賴。如果用 Spring Boot,直接加: 1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId></dependency> 這個依賴裡面已經包含了 AspectJ 和 Spring AOP 相關的東西。 基本結構用 Spring AOP 的步驟很簡單: 寫一個類別標注 @Aspect 標注 @Component 讓 Spring 掃描到 在方法上標注 @Before、@After 等 定義切入點(point...
Spring Bean Scope 六種作用域完整解析
之前在專案裡因為搞不清楚 Bean scope,導致一個坑埋了好久才發現。乾脆就把 Spring 的六種 scope 整理一下,這樣以後不會再踩到相同的坑。 六種 Scope 速覽Spring Bean 的六種 scope 分別是: singleton - 預設,整個 container 只有一個實例 prototype - 每次 getBean 都新建一個實例 request - 每個 HTTP request 一個實例 session - 每個 HTTP session 一個實例 application - 整個 ServletContext 一個實例 websocket - 每個 WebSocket session 一個實例 後面四種(request、session、application、websocket)只在 web application 中可用。 Singleton 作用域這是預設的 scope。如果你沒有特別指定,Spring 就會把 Bean 當成 singleton: 123456@Componentpublic class UserService ...














