结论
对于原型Bean,IoC容器并没有进行缓存,所以每次调用getBean方法都是重新创建Bean,与线程无关。
代码测试
编写一个类,添加上@Scope注解,然后指定其value为prototype。创建注解驱动应用上下文,调用register方法来注册编写的类的class,通过应用上下文的getBean(Class)方法来获取应用上下文创建好的Bean实例。
package com.xxx.fame.prototype;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;
@Scope(ConfigurableListableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBeanDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(PrototypeBeanDemo.class);
context.refresh();
PrototypeBeanDemo prototypeBeanDemo = context.getBean(PrototypeBeanDemo.class);
PrototypeBeanDemo prototypeBeanDemo2 = context.getBean(PrototypeBeanDemo.class);
System.out.println("第一次获取到的 PrototypeBeanDemo 实例为:" + prototypeBeanDemo);
System.out.println("第二次获取到的 PrototypeBeanDemo 实例为:" + prototypeBeanDemo2);
}
}
运行结果:
可以看到每次通过getBean(Class)方法获取到的实例都是不一样的。
之所以会出现这种问题,是因为IoC容器的doCreateBean方法所决定的。doCreateBean方法由Abstr-actBeanFactory类实现,在该方法中,首先根据beanName调用getSingleton方法(底层是去IoC容器中的一级缓存、二级缓存、三级缓存查找),因为原型Bean是不会被缓存的,所以这里获取到的为空。
接下来根据beanName去获取BeanDefinition数据,然后根据获取到的BeanDefinition的isSingleton方法来判断是否是单例的,由于我们指定Scope为原型(Prototype),这里判断失败,接下来调用BeanD-efinition的isPrototype方法来判断是否是一个原型Bean,判断成立。
首先调用beforePrototypeCreation方法来标记该beanName正在创建(因为Bean的创建是一个很耗时的操作,所以需要提前进行标记),接下来就调用createBean方法真正地进行Bean创建,在finally块中。看上去创建原型Bean和创建单例Bean没什么差别,最终都是调用createBean方法,问题就出在getSingleton方法上。
// AbstractBeanFactory#doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 只保留和本次分析相关代码,省略其它代码...
} else {
// 只保留和本次分析相关代码,省略其它代码...
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 只保留和本次分析相关代码,省略其它代码...
// 创建单例Bean
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
// 创建原型Bean
Object prototypeInstance = null;
try {
// 标记指定beanName正在创建
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
} finally {
// 移除在beforePrototypeCreation方法中对beanName的标记
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// 只保留和本次分析相关代码,省略其它代码...
return (T) bean;
}
getSingleton方法由DefaultSingletonBeanRegistry类定义并实现,在该实现中,首先对一级缓存-sin-gletonObject进行加锁,然后再次根据beanName从一级缓存中获取(典型的Double Check),如果获取到的结果还是等于null,调用beforeSingletonCreation方法来标记该beanName正在进行创建,在try代码块中调用传入的ObjectFactory实例的getObject方法,由于使用的是Lambda表达式,所以实际调用的还是createBean方法。
注意接下来的将newSingleton标志位设置为true,这点非常重要,因为在方法的最后就是根据判断该标志位是否为true,来决定要不要调用addSingleton方法。
// DefaultSingletonBeanRegistry#getSingleton
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
// Double Check,双重检查
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
// 调用 beforeSingletonCreation 方法标记该beanName 正在创建
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 调用传入的ObjectFactory实例的getObject方法,这里使用的Lambda表达式,实际调用的是createBean方法
singletonObject = singletonFactory.getObject();
// 注意这里将newSingleton标志位设置为true,这点非常重要,因为IoC容器就是根据该
// 标志位来决定是否将bean添加到一级缓存中。
newSingleton = true;
} catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
} catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
} finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
// 如果newSingleton标志位为true,则调用addSingleton方法
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
addSinglton方法同样由DefaultSingletonBeanRegistry定义并实现,该实现非常简单,就是将创建好的bean实例保存到singletonObjects中,并从singletonFactories(三级缓存)以及earlySingletonObject-s(二级缓存)中移除,最后在registeredSingletons集合也保存一份beanName。
// DefaultSingletonBeanRegistry#addSingleton
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
总结
在一个线程内多次获取原型Bean,每次返回的Bean实例都是不同的,之所以这样是因为对于原型Bean,IoC容器是不会进行存储的,和线程没有任何关系。
这样的设计也是合理的,因为每次请求原型Bean时IoC容器都需要创建,如果还保留其引用,那么因为强引用而导致垃圾回收器无法进行GC(可达性分析算法),从而导致内存泄漏,另外既然每次请求都需要创建,IoC容器保存对象引用的意义又何在呢?