Spring中的Singleton模式和Java中的Singleton模式
对于Spring中实现Singleton模式,是以IOC容器为单位,换句话说,一个JVM可能有多个IOC容器,而Java中实现Singleton而言,只有一个JVM。所以说,Spring中实现的Singleton模式与Java中的Singleton设计模式有点不同。
此外,springsingleton的含义是在一个spring上下文中保持单态。
所以singleton模式中,singleton的class在整个JVM中只有一个instance,Spring的Bean,你可以一个class配置多个Bean,这个class就有了多个instance。这个singleton是指在spring容器中,这个Bean是单实例的,是线程共享的。所以要求这些类都是线程安全的。也就是说,不能出现修改Bean属性的方法,当然除了设值得那些setter。只要满足线程安全,这些bean都可以用singleton。而且我们在绝大多数使用上,也是这样用的,包括dao,service。
Beanfactory是Spring初始以静态方式载入的,Spring的单例IOC是基于容器级的,所以这你都不用担心与考虑.
--应用中对象有两种,行为对象和数据对象,行为对象都要求是线程安全的!也就是允许单例的,不管是dao还是service对象,都是行为对象,行为对象不应该引用非线程安全的对象做成员量,同时在应用外部的资源(如文件,数据库连接,session)时,要先保证对这些东西的访问是做了并发控制的!
对于spring来讲,<beanscope="singleton"/>或<beansingleton="true"/>都是保证对同一sesionfactorybean是单例的,也就是所谓sessionfactory范围的.
--这是一个真实的案例,我们在项目中使用Spring和ACEGI,我之所以选择ACEGI,除了它对权限的良好控制外,
我还看好它的SecurityContextHolder,通过代码来看:
Authentication auth = SecurityContextHolder.getContext().getAuthentication(); <script>render_code();</script>
我可以很容易在系统任意一层得到用户的信息,而不用把用户信息在参数里传来传去,(这也是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(); } <script>render_code();</script>
这样在其他的Service和Dao类里可以通过
super.getUserContext(), super.getUserInfo() <script>render_code();</script>
来得到用户的信息,这也为问题的产生提供了温床。请看如下代码:
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 } } <script>render_code();</script>
这段代码在单元测试的时候不会用任何问题,但是在多用户测试的情况下,你会发现任何调用SomeService里someMethod()方法的userID和userName都是同一个人,也就是第一个登陆的人的信息。
其根本原因是Spring的Bean在默认情况下是Singleton的,BeanSomeServece的实例只会生成一份,也就是所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 } }