论坛首页 Java企业应用论坛

Spring Singleton的陷阱

浏览 26892 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2006-12-22  
这是一个真实的案例,我们在项目中使用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
      }
  }    
   发表时间:2006-12-22  
刚刚看了一下,挑一点小刺:
1、默认情况下是singletom
2、真的案例
3、"使用SpringACEGI"---好像ACEGI是现在spring的子项目哦...
...
0 请登录后投票
   发表时间:2006-12-23  
lighter 写道
刚刚看了一下,挑一点小刺:
1、默认情况下是singletom
2、真的案例
3、"使用SpringACEGI"---好像ACEGI是现在spring的子项目哦...
...

谢谢提醒,昨天发现的问题,马上就记录下来了,可能写的有点快,敲错了
0 请登录后投票
   发表时间:2006-12-23  
Spring建议以singleton方式注入的依赖尽量是stateless的
0 请登录后投票
   发表时间:2006-12-23  
可以修改配置文件,修改参数吧!!
0 请登录后投票
   发表时间:2006-12-23  
用 ThreadLocal 来保存用户信息
0 请登录后投票
   发表时间:2006-12-23  
这不是Spring 的问题,跟本问题要搞清楚,
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  
    }  
}      

不能在service类里面写这种东西。居然有人说要用ThreadLocal...无语。
0 请登录后投票
   发表时间:2006-12-23  
tiyi 写道
用 ThreadLocal 来保存用户信息

SecurityContext本身就是用ThreadLocal实现的,再来个ThreadLocal?
0 请登录后投票
   发表时间:2006-12-23  
ecsun 写道
改改配置,写个全局变量,什么问题也就解决了,哪有这么麻烦

用全局变量来解决?
0 请登录后投票
   发表时间:2006-12-23  
Godlikeme 写道
这不是Spring 的问题,跟本问题要搞清楚,
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  
    }  
}      

不能在service类里面写这种东西。居然有人说要用ThreadLocal...无语。

没看明白你的意思!
用ThreadLocal存当前用户很奇怪吗?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics