文章目录
Spring IOC循环依赖问题
什么是循环依赖
循环依赖其实就是循环引用,也就是两个或者两个以上的Bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A:
注意:这里不是函数的循环调用,而是对象的相互依赖关系。循环调用其实就是一个死循环,除非有终结条件
Spring 中循环依赖场景有:
- 构造器的循环依赖(构造器注入)
- 成员属性的循环依赖(set注入)
其中,构造器的循环依赖问题无法解决,只能抛出 BeanCurrentlyCreationException
异常,在解决属性循环依赖时,spring采用的是提前暴露对象的方法。
循环依赖处理机制
无法解决的循环依赖
对于多例 prototype 原型 Bean 的初始化过程中,不论是构造器参数循环依赖,还是通过setXXX方法产生循环依赖,spring都是直接报错处理。原因很好理解,创建新的A时,发现要注入原型字段B,又创建新的B发现要注入原型字段A…
AbstractBeanFactory#doGetBean
方法
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
在获取bean之前,如果这个原型bean正则被创建则直接抛出异常。原型bean在创建之前会进行标记这个beanName正在被创建,等创建结束之后会 删除标记
AbstractBeanFactory#doGetBean
方法
try {
// 创建原型bean之前添加标记
beforePrototypeCreation(beanName);
// 创建原型bean
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
// 创建原型bean之后删除标记
afterPrototypeCreation(beanName);
}
总结:Spring不支持原型Bean的循环依赖
单例 Bean 构造器参数循环依赖(无法解决)
Spring的循环依赖的解决方式基于Java的引用传递,单例Bean使用构造器参数产生的循环依赖问题,构造器是在获得引用之前的,无法产生实例对象,也无法获得引用对象,所以无法解决。具体原因可以看下去,看看spring是如何解决循环依赖的方法,就容易理解了
Spring循环依赖的解决方法
spring的循环依赖的理论基于Java的引用传递,当获得对象的引用时,对象的属性是可以延后设置的。
spring能解决的循环依赖的场景是 通过setXXX方法或者@Autowired 方法注入依赖对象,其实是通过提前暴露一个ObjectFactory对象来完成的。
首先,Spring 内部维护了三个Map ,也就是我们通常说的三级缓存,但是Spring的官方文档却没有找到三级缓存相关的概念,可能是因为这三个Map的注释都以Cache of 开头吧
在DefaultSingletonBeanRegistry
中,声明了这三个Map:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
......
}
-
singletonObjects :
它应该是我们最熟悉的了,俗称 “单例池”、“容器”,创建完成的单例Bean就放在这里地方
-
singletonFactories:
映射创建Bean的原始工厂
-
earlySingletonObjects:
映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance.
后两个Map其实是“垫脚石”级别的,只是创建Bean的时候,用来借助了一下,创建完成就清掉了。
先看下Spring解决循环依赖的简图
简单总结下:
-
如何检测是否有循环依赖?
Bean在创建的时候给其打个标记,如果递归调用回来发现正在创建中的话—>即可说明循环依赖。
-
Spring是怎么解决的循环依赖?
Spring的循环依赖的理论依据其实是基于Java的引用传递,当我们获取到对象的引用时,对象的field属性是可以延后设置的(但是构造器必须是在获取引用之前)。
spring bean 的单例对象的初始化主要分为下面三步:
createBeanInstance
实例化 ==>populateBean
填充属性 ==>InitializeBean
初始化从上面讲述的单例bean初始化步骤我们可以知道,循环依赖主要发生在第一、第二步。也就是构造器循环依赖和field循环依赖。
那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存。
在
AbstractAutowireCapableBeanFactory#doCreateBean
方法中,这个时候实例化了对象,未设置属性if (instanceWrapper == null) { // 创建bean实例,仅仅调用构造方法,但是尚未设置属性 instanceWrapper = createBeanInstance(beanName, mbd, args); }
然后加入三级缓存
if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } // 加入三级缓存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
加入三级缓存方法:
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
这里就是解决循环依赖的关键,这段代码发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)。这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用。
接着注入属性:
// 初始化bean实例 Object exposedObject = bean; try { // bean属性填充 populateBean(beanName, mbd, instanceWrapper); // 调用初始化方法,应用BeanPostProcessor后置处理器 exposedObject = initializeBean(beanName, exposedObject, mbd); }
AbstractAutowireCapableBeanFactory#populateBean
然后注入属性发现缺少依赖对象if (pvs != null) { applyPropertyValues(beanName, mbd, bw, pvs); }
AbstractAutowireCapableBeanFactory#applyPropertyValues
获取依赖对象的引用值Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
BeanDefinitionValueResolver#resolveReference
方法会执行getBean方法resolvedName = String.valueOf(doEvaluate(ref.getBeanName())); bean = this.beanFactory.getBean(resolvedName);
这个时候就是正常的创建所依赖的Bean B了,调用
doGetBean
,然后又来到了AbstractAutowireCapableBeanFactory#doCreateBean
方法// 初始化bean实例 Object exposedObject = bean; try { // bean属性填充 populateBean(beanName, mbd, instanceWrapper); // 调用初始化方法,应用BeanPostProcessor后置处理器 exposedObject = initializeBean(beanName, exposedObject, mbd); }
AbstractAutowireCapableBeanFactory#applyPropertyValues
这个时候设置Bean B的属性AString propertyName = pv.getName();// 类B所依赖的ClassA Object originalValue = pv.getValue(); // 类B所依赖的classA ...... Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
来到
BeanDefinitionValueResolver#resolveReference
获取Bean AresolvedName = String.valueOf(doEvaluate(ref.getBeanName())); bean = this.beanFactory.getBean(resolvedName);
又走到了
getBean
=>doGetBean
方法,但是这个时候,已经能获取到ClassA了AbstractBeanFactory#doGetBean
方法中// 尝试从缓存中获取Bean Object sharedInstance = getSingleton(beanName);
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; }
由于之前实例化ClassA后,把它放到了三级缓存中了,所以这时候ClassB依赖ClassA的问题已经解决,Bean B成功创建,BeanA自然也就创建成功了。
-
为什么要把ClassA从三级缓存放到二级缓存中
if (singletonFactory != null) { // 把三级缓存放到二级缓存中 singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); }
这里传入的是一个Lambda表达式
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
这时候就可以做一些扩展操作 如BeanPostProcessor操作
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); } } return exposedObject; }
至此,spring循环依赖的问题解决!