文章目录
- 单例之间set注入允许
- 非单例无法循环依赖
单例之间set注入允许
首先下一个结论:单例之间,通过set注入是允许循环引用的。
是用单例三级缓存来解决循环依赖的。Spring容器创建单例bean分为三步:
第一 实例化;
第二 设置属性;
第三 调用生命周期回调函数。
第一级缓存单例对象池singletonObjects,存放完全初始化好的bean。所有属性设置完全、所有的生命周期回调完成;
第二级缓存早期单例对象池earlySingletonObjects,里面是实例化,但是未填充属性的bean;
第三级缓存是对象工厂缓存singletonFactories。如果说不用FactoryBean的话,我一般这级缓存就用不上。
正在创建的bean的名称都会放在singletonsCurrentlyInCreation集合中,这个集合不是bean的集合是bean名称的set,一个Set<String>。将完全初始化完成的和仅仅实例化但是未设置属性的区分开。
第一步是往第三级缓存里加,方法为:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)
工厂是org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean这个方法以λ表达式提供的。
第二步是是执行populateBean方法,在
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
在populateBean里注入依赖的属性。源码如下:
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
&emsp 这两步就是实现了从三级缓存到一级缓存的bean的迁移。最后一步就是处理后置事件了,也就是执行BeanPostProcessor。Nacos等spring cloud配置中间件客户端,原理也是注册自己的BeanPostProcessor来实现配置注入的。
&emsp 所以简单说来,将bean的创建和字段的注入 分开之后,就解决了循环依赖的问题了。
其加入有AB两个bean循环依赖,顺序是这样的:
- 构造bean A;
- 设置属性bean A的属性b时发现没有bean B,则构造bean B;
- 设置属性bean B的属性a为已完成构造的A;
- 设置属性bean A的属性b为上两步已经完成了的bean B。
非单例无法循环依赖
上面讲的是单例Bean,至于非单例Bean的互相依赖,我们做个小实验,先写Bean A:
package com.youngthing.springboot.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
/**
* 10/12/2022 4:23 PM 创建
*
* @author 花书粉丝
*/
@Scope("prototype")
@Component
public class BeanA {
public BeanB getBeanB() {
return beanB;
}
public void setBeanB(BeanB beanB) {
this.beanB = beanB;
}
@Autowired
private BeanB beanB;
}
再写Bean B:
package com.youngthing.springboot.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
/**
* 10/12/2022 4:24 PM 创建
*
* @author 花书粉丝
*/
@Scope("prototype")
@Component
public class BeanB {
@Autowired
private BeanA beanA;
public BeanA getBeanA() {
return beanA;
}
public void setBeanA(BeanA beanA) {
this.beanA = beanA;
}
}
再写个实验方法:
package com.youngthing.springboot.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
/**
* 3/19/2022 11:30 AM 创建
*
* @author 俞建波
*/
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
final ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
BeanA bean = context.getBean(BeanA.class);
System.out.println(bean);
System.out.println(bean.getBeanB());
bean = context.getBean(BeanA.class);
System.out.println(bean);
System.out.println(bean.getBeanB());
}
}
执行一下,直接报错(我省略了前面很多行的输出):
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'beanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:274)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1380)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:656)
... 27 more
Process finished with exit code 1
最重要的是后面一句话:Requested bean is currently in creation: Is there an unresolvable circular reference?
那为什么非单例Bean不行呢?仔细思考上面的四步,就会发现在第三步会出问题:
- 构造bean A;
- 设置属性bean A的属性b时发现没有bean B,则构造bean B;
-
设置属性bean B的属性a时,由于不是单例bean,所以无法精确找到到底是哪个A的实例
而spring的源码是特意在这里做了校验的,如果非单例,则检查是否循环引用,存在循环引用则抛出异常,在org.springframework.beans.factory.support.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);
}