Spring获取单例bean方法的思考
使用的SpringMVC版本是5.1.8.RELEASE
下面是DefaultSingletonBeanRegistry类中通过类名获取单例的源码
/** 单例对象的缓存:从bean名称到bean实例 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 单例对象的缓存:从bean名称到objects实例工厂 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** 早期单例对象的缓存:从bean名称到bean实例 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/**
* 返回以给定名称注册的(原始)singleton对象
* <p> 检查已经实例化了singleton,还允许对当前创建的singleton进行早期引用(解析循环引用)
* @param beanName 要查找的bean的名称
* @param allowEarlyReference 是否应创建早期引用
* @return 已注册的singleton对象,或者如果未找到任何对象返回null
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
/**
* 返回一个指定的bean实例,可以是共享的,也可以是依赖的
* @param name 要检索的bean的名称
* @param requiredType 需要检索的bean类型
* @param args 使用显式参数创建bean实例时要使用的参数
* (仅在创建新实例而不是检索现有实例时应用)
* @param typeCheckOnly 是否获取实例进行类型检查,而不是实际使用
* @return bean实例
* @throws BeansException 当bean实例无法创建时抛出
*/
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所依赖的bean的初始化.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// Create bean instance.
if (mbd.isSingleton()) {
// 核心方法:getSingleton(String beanName, ObjectFactory<?> singletonFactory)
sharedInstance = getSingleton(beanName, () -> {
try {
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);
}
else if (mbd.isPrototype()) {
// 忽略...
}
else {
// 忽略...
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// 忽略...
return (T) bean;
}
/**
* 获取给定bean实例的对象,如果是factorybean,则为bean实例本身或其创建的对象
* @param beanInstance 共享的bean实例
* @param name 可能包含工厂取消引用前缀的名称
* @param beanName 标准bean名称
* @param mbd 合并的bean定义
* @return 为bean暴露的对象
*/
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// Don't let calling code try to dereference the factory if the bean isn't a factory.
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
}
// Now we have the bean instance, which may be a normal bean or a FactoryBean.
// If it's a FactoryBean, we use it to create a bean instance, unless the
// caller actually wants a reference to the factory.
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
Object object = null;
if (mbd == null) {
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
/**
* 将给定的singleton对象添加到此工厂的singleton缓存中
* <p> 在需要注册单例时被调用.
* @param beanName bean的名称
* @param singletonObject 单例对象
*/
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName); // 无论如何都会在这里释放它,不管它是否已经转移到earlySingletonObjects
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
- 对象间的依赖
- 构造函数传参
public class TestBean1 {
private TestBean2 testBean2;
TestBean2(){}
TestBean2(TestBean2 testBean2){
this.testBean2 = testBean2;
}
}
- setter方法传参
public class TestBean1 {
private TestBean2 testBean2;
public void setTestBean2(TestBean2 testBean2){
this.testBean2 = testBean2;
}
public void getTestBean2(){
return this.testBean2;
}
}
- 循环依赖问题
循环依赖,bean之间相互持有各自的引用,最终形成闭环。
bean的实例化总共分三步:
- 调用构造函数实例化,分配内存空间,并对属性赋默认值;createBeanInstance
- 设置属性;populateBean
- 最终完成实例化;InitializeBean
-
三级缓存解决循环依赖
spring使用了三个Map类型的缓存变量:singletonObjects、singletonFactories、earlySingletonObjects来解决单例类实例化的循环依赖问题。但spring仍然无法解决构造器依赖和原型类依赖的问题。
由于创建实例第一步就是执行构造函数,如果此时需要依赖于某个类的实例而spring又无法将当前正在实例化中的类提前曝光(还未获得堆内存的引用地址),则出现循环依赖异常;另外,spring三级缓存机制只针对类型为singleton的类,prototype类型需要每次单独创建实例,因此其属性值都需要显式调用setter来注入。 -
构造器和setter函数上的@Autowired注解解决循环依赖
在高版本的spring中已经可以支持构造函数和setter方式完成注入了,代码如下:
public class TestBean1 {
private final TestBean2 testBean2;
// 这种方式会导致构造器非常丑陋,没有在属性上直接添加注解来得简洁优雅
@Autowired
public TestBean1(TestBean2 testBean2){
this.testBean2 = testBean2;
}
}
public class TestBean1 {
private TestBean2 testBean2;
// 这种方式非常灵活,后期还可以进行重新注入,但代码量仍然不少
@Autowired
public setTestBean2(TestBean2 testBean2){
this.testBean2 = testBean2;
}
}
总结
- 三级缓存解决循环依赖
创建实例的过程,使用了三个Map类型的缓存变量:singletonObjects、singletonFactories、earlySingletonObjects ,其中单例对象集合使用线程安全的Map创建。通过getSingleton()方法的源码分析发现,获取bean实例时先从单例对象集合中找,再找早期单例集合,最后找单例工厂集合,如果从单例工厂中找到则将此bean实例从单例工厂集合中移除并放到早期单例集合中存储,那么bean实例什么时候进入的单例工厂集合,又是什么时候进入的单例对象集合中呢?
通过doGetBean()方法可以定位到创建实例的方法正是getSingleton(String beanName, ObjectFactory<?> singletonFactory)的返回值,而且这个方法参数需要一个ObjectFactory的对象,该类添加了@FunctionalInterface注解,说明这是一个函数式接口,其getObject()返回的值正式通过createBean()方法创建出的bean实例。所以分三步读源码:
- 了解createBean创建一个实例的过程
- 了解getSingleton()方法在回调获取到bean实例后做了哪些处理
- 了解getObjectForBeanInstance()方法获取完整的实例对象
进而发现,创建实例时通过addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))将此实例(仅仅是个早期bean的引用)先保存到单例工厂集合内,创建实例完成之后通过addSingleton(beanName, singletonObject)方法将此实例保存到单例对象集合内,同时从单例工厂和早期单例集合中移除。
那么,将创建的实例临时性的保存在早期单例集合的是为什么呢?为什么呢?不知道
从设计理念上来说,singletonObjects持有的应该是完整的实例,singletonFactories是还在工厂进行生产的实例,如果此时需要被依赖,这两种实例都不合适,所以就需要增加一种可以提前将自己曝光(被依赖)的实例,所以就有了早期单例集合来保存它,同时如果早期单例集合已经持有了某个实例,单例工厂集合也就没必要再继续持有,可以直接释放(remove)掉,在创建实例的最后一步添加到单例对象集合中时也会释放它。
-
spring如何实现的构造器注入和
-
阅读