Spring获取单例bean方法的思考

本文深入探讨了Spring如何通过DefaultSingletonBeanRegistry获取单例bean,特别是涉及循环依赖的解决策略。分析了三级缓存(singletonObjects、singletonFactories、earlySingletonObjects)在处理循环依赖中的作用,并解释了为什么需要早期单例对象集合。文章还讨论了@Autowired注解在构造函数和setter方法中解决循环依赖的方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
	}
}
  • 对象间的依赖
  1. 构造函数传参
public class TestBean1 {
	private TestBean2 testBean2;
	TestBean2(){}
	TestBean2(TestBean2 testBean2){
		this.testBean2 = testBean2;
	}
}
  1. setter方法传参
public class TestBean1 {
	private TestBean2 testBean2;
	public void setTestBean2(TestBean2 testBean2){
		this.testBean2 = testBean2;
	}
	public void getTestBean2(){
		return this.testBean2;
	}
}
  • 循环依赖问题
    循环依赖,bean之间相互持有各自的引用,最终形成闭环。

bean的实例化总共分三步:

  1. 调用构造函数实例化,分配内存空间,并对属性赋默认值;createBeanInstance
  2. 设置属性;populateBean
  3. 最终完成实例化;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实例。所以分三步读源码:
  1. 了解createBean创建一个实例的过程
  2. 了解getSingleton()方法在回调获取到bean实例后做了哪些处理
  3. 了解getObjectForBeanInstance()方法获取完整的实例对象

进而发现,创建实例时通过addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))将此实例(仅仅是个早期bean的引用)先保存到单例工厂集合内,创建实例完成之后通过addSingleton(beanName, singletonObject)方法将此实例保存到单例对象集合内,同时从单例工厂和早期单例集合中移除。
那么,将创建的实例临时性的保存在早期单例集合的是为什么呢?为什么呢?不知道

从设计理念上来说,singletonObjects持有的应该是完整的实例,singletonFactories是还在工厂进行生产的实例,如果此时需要被依赖,这两种实例都不合适,所以就需要增加一种可以提前将自己曝光(被依赖)的实例,所以就有了早期单例集合来保存它,同时如果早期单例集合已经持有了某个实例,单例工厂集合也就没必要再继续持有,可以直接释放(remove)掉,在创建实例的最后一步添加到单例对象集合中时也会释放它。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值