一.问题
在spring中,Bean的scope默认为singleton。可能我们会把spring的singleton与设计模式中的singleon
类比然后等价。事实是:在spring中的singleton不是以单例模式创建,而是在容器中以单例存在。
二.源码分析(这里以spring3.2.0为例)
创建一个示例,通过调试找到入口在:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean中,这里只贴出主要部分。
如果实例已经存在,则直接取出使用:
//获取已经注册的实例
Object sharedInstance = getSingleton(beanName);
//实例已经存在
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//如果缓存中已经存在,直接取出
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
如果实例还不存在,则进行创建:
//如果正在创建,异常返回
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//scope==singleton的创建
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
try {
//createBean()反射创建实例
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
//创建异常则销毁
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//scope==prototype的创建方式
else if (mbd.isPrototype()) {
//创建新实例
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
//创建前的回调:说明实例已经在创建
beforePrototypeCreation(beanName);
//反射创建实例
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
//创建后的回调:标记实例不存在,因为是多例创建
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
//后面还有其他scope的创建
......
spring中的单例是只在容器中的存在方式,而不是创建方式。
三.singleton线程安全问题
spring框架中的单例Bean不是线程安全的。
spring框架将单例的Bean进行统一管理,但是允许多线程访问。所以对于无状态的Bean(没有实例变量的对象,比如:dao层的Bean),使用singleton是线程安全的;但是对于有状态的Bean(就是有实例变量的对象,比如:模型层的对象),使用singleton就是线程不安全的,解决方法可以是(a):时间换空间:线程同步机制;(b)空间换时间:改用prototype模式;或者使用ThreadLocal。
对于有状态的Bean使用prototype模式比较方便。