這是一個真實的案例,我們在項目中使用Spring和ACEGI,我之所以選擇ACEGI,除了它對權限的良好控制外,
我還看好它的SecurityContextHolder,通過代碼
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
我可以很容易在系統任意一層得到用戶的信息,而不用把用戶信息在參數裡傳來傳去,(這也是struts的缺點之一)
但是我在每一次要得到用戶信息的時候都寫上面的一段代碼,未免有些麻煩,所以我在BaseService, BaseDao裡都提供了如下方法:
/**//**
* get current login user info
* @return UserInfo
*/
protected UserInfo getUserInfo()
...{
return getUserContext().getUserInfo();
}
/**//**
* get current login user context
* @return UserContext
*/
protected UserContext getUserContext()
...{
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return (UserContext) auth.getPrincipal();
}
這樣在其他的Service和Dao類裡可以通過
super.getUserContext(), super.getUserInfo()
來得到用戶的信息,這也為問題的產生提供了溫床。請看如下代碼:
public class SomeServece extends BaseService implements SomeInterFace
...{
private UserInfo user = super.getUserInfo();
public someMethod()
...{
int userID = this.user.getUserID();
String userName = this.user.getUserName();
//bla bla do something user userID and userNaem
}
}
這段代碼在單元測試的時候不會用任何問題,但是在多用戶測試的情況下,你會發現任何調用SomeService裡someMethod()方法的userID和userName都是同一個人,也就是第一個登陸的人的信息。Why?
其根本原因是Spring的Bean在默認情況下是Singleton的,Bean SomeServece的實例只會生成一份,也就是所SomeServece實例的user 對象只會被初始化一次,就是第一次登陸人的信息,以後不會變了。所以BaseService想為開發提供方便,確給開發帶來了風險正確的用法應該是這樣的
public class SomeServece extends BaseService implements SomeInterFace
...{
public someMethod()
...{
int userID = super.getUserInfo().getUserID();
String userName = super.getUserInfo().getUserName();
//bla bla do something user userID and userNaem
}
}