項目使用 Spring + Acegi Security。為便捷獲取當前登錄用戶,在 BaseService/BaseDao 提供:
protected UserContext getUserContext() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return (UserContext) auth.getPrincipal();
}
protected UserInfo getUserInfo() { return getUserContext().getUserInfo(); }
子類 SomeService 為「優化」在字段緩存用戶信息:
public class SomeService extends BaseService {
private UserInfo user = super.getUserInfo(); // 陷阱在此!
public void someMethod() {
int uid = this.user.getUserId(); // 永遠是第一個登錄用戶
...
}
}
SomeService 實例user 在 Bean 初始化/依賴注入階段賦值(第一次調用 getUserInfo())
public class SomeService extends BaseService {
public void someMethod() {
// 每次調用實時從 ThreadLocal (SecurityContextHolder) 獲取
int uid = super.getUserInfo().getUserId();
String name = super.getUserInfo().getUserName();
...
}
}
SecurityContextHolder 內部使用 ThreadLocal 隔離線程上下文,每次調用獲取的是當前請求線程綁定的用戶,天然線程安全。
| 作用域 | 生命周期 | 適用場景 | 狀態風險 |
|---|---|---|---|
| singleton (默認) | 容器級,唯一實例 | 無狀態 Service、DAO、Controller、工具類 | 高:嚴禁持有請求/會話級數據 |
| prototype | 每次獲取新實例 | 有狀態對象、命令模式、重量級配置對象 | 低:但需自行管理銷毀 |
| request | 單次 HTTP 請求 | Web 層表單對象、請求級緩存 | 無 |
| session | 用戶會話 | 購物車、用戶偏好 | 無 |