當對象包含可變狀態 (且該狀態在實際業務中會被修改) 時 → Prototype (多例)
否則 → Singleton (單例)
@Service // 默認 singleton
@Scope("prototype") // 或 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class MyAction { ... }
| 選擇 | 理由 |
|---|---|
| Singleton | 避免重複創建開銷 (CPU/內存/GC);無狀態對象天然線程安全;依賴注入圖簡單高效 |
| Prototype | 隔離可變狀態,防止併發請求數據污染;每個請求/任務擁有獨立上下文 |
| 層/組件 | 推薦 Scope | 理由 | 反例後果 |
|---|---|---|---|
| Controller (Spring MVC) | Singleton | 參數通過方法傳遞,無實例字段狀態 | 加字段存請求參數 → 併發錯亂 |
| Service | Singleton | 業務邏輯無狀態,只協調 Dao | 在 Service 中存用戶信息 → 數據洩露 |
| Dao / Repository | Singleton | 數據訪問無狀態,線程安全 | 極少見,除非 Dao 持有連接/事務狀態 |
| Struts2 Action | Prototype | 框架設計:Action 實例字段綁定請求參數 | Singleton → 參數覆蓋、驗證錯誤共享 |
| Struts1 Action | Singleton | 參數在 ActionForm 中,Action 無狀態 | — |
| 有狀態工具類 | Prototype | 如報表生成器、線程上下文持有者 | Singleton → 數據混亂 |
| 請求/會話作用域 | request / session | Web 環境專用,生命周期綁定 HTTP | — |
@Service
public class OrderService {
// 危險!實例字段存儲請求相關數據
private String currentUserId;
private List cachedOrders;
public void setUser(String userId) { this.currentUserId = userId; }
public List getOrders() { return cachedOrders; }
}
**後果**:併發請求 A 設置 userId=1,請求 B 設置 userId=2 → A 獲取訂單時可能拿到 B 的數據。**極難復現、極難定位**。
**修正**:參數傳遞、ThreadLocal (謹慎)、RequestScope Bean。
無狀態 → Singleton (默認、高效)
有狀態 → Prototype (隔離、安全)
Web 請求 → request/session (框架原生)
Service/Dao → 默認 Singleton,堅決不加字段存請求數據