循环依赖
循环依赖其实就是循环引用,即两个或者两个以上的bean互相持有对方,最终形成闭环
两大场景

第一种:构造器注入循环依赖
@Servicepublic class AService {public AService(BService bService) { }}@Servicepublic class BService {public BService(AService aService) { }}
结果:
The dependencies of some of the beans in the application context form a cycle:┌─────┐| AService defined in file [D:\Java\springboot\springboot\target\classes\top\parak\depend\AService.class]↑ ↓| BService defined in file [D:\Java\springboot\springboot\target\classes\top\parak\depend\BService.class]└─────┘
Spring解决循环依赖依靠的是Bean的中间态,而这个中间态是指已经实例化,但未初始化。构造器执行是在中间态之前,因此构造器的循环依赖无法解决。
第二种:单例的setter注入
@Servicepublic class AService {@Autowiredprivate BService bService;}@Servicepublic class BService {@Autowiredprivate AService aService;}
这种场景经常使用,没有问题。
第三种:多例的setter注入
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)@Servicepublic class AService {@Autowiredprivate BService bService;}@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)@Servicepublic class BService {@Autowiredprivate AService aService;}@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)@Componentpublic class CService {@Autowiredprivate AService aService;}
结果:
The dependencies of some of the beans in the application context form a cycle:c (field private top.parak.depend.AService top.parak.depend.C.aService)┌─────┐| AService (field private top.parak.depend.BService top.parak.depend.AService.bService)↑ ↓| BService (field private top.parak.depend.AService top.parak.depend.BService.aService)└─────┘
没有使用缓存,每次都会生成一个新对象。
总结
- 构造器注入和prototype类型的field注入发生循环依赖时都无法初始化
- field注入单例的bean时,尽管有循环依赖,但是bean可以成功初始化
如何检测
Bean在创建的时候可以给该Bean打个标志,如果递归调用回来发现正在创建中的话,即说明产生了循环依赖。
如何解决
DefaultSingletonBeanRegistry
内部属性:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {/** 一级缓存 */private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);/** 三级缓存 */private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);/** 二级缓存 */private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);/** 保存所有已经注册的bean的名字 */private final Set<String> registeredSingletons = new LinkedHashSet<>(256);/** 标识指定名字的bean对象是否处于创建状态 */private final Set<String> singletonsCurrentlyInCreation =Collections.newSetFromMap(new ConcurrentHashMap<>(16));// ....}
三级缓存:
| 缓存 | 级别 | 用途 |
|---|---|---|
| singletonObjects | 一级 | 存放最终单例,key为bean名称,value为bean实例。这里的bean实例指的是已经完全创建好的,即已经经历实例化->属性填充->初始化->后置处理过程的bean,可以直接使用。 |
| earlySingletonObjects | 二级 | 存放早期对象,key为bean名称,value为bean实例。这里的bean实例指的是仅完成实例化的bean,还未进行属性填充等后续操作。用于提前曝光,供别的bean引用,解决循环依赖。 |
| singletonFactories | 三级 | 存放回调方法,key为bean名称,value为bean工厂。在bean实例化之后,属性填充之前,如果允许提前曝光,Spring会把该bean转换为bean工厂并加入到三级缓存。在需要引用提前曝光对象时再通过工厂对象的getObject方法获取。 |
源码分析
IOC容器获取bean的入口为AbstractBeanFactory的getBean方法:
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {@Overridepublic Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false);}// 真正实现protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {String beanName = transformedBeanName(name);Object bean;// 先去获取一次,如果不为null,此处就会走缓存Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {// 不为空,则进行后续处理并返回bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);} else {...try {...RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);...// 从三级缓存中没有获取到Bean实例,并且目标Bean是单实例Bean的话if (mbd.isSingleton()) {// 通过getSingleton方法创建Bean实例sharedInstance = getSingleton(beanName, () -> {try {// 创建Bean实例return createBean(beanName, mbd, args);}});// 后续处理,并返回bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}...}}return (T) bean;}}
首先通过getSingleton(String beanName)方法从三级缓存中获取bean实例,如果不为空则进行后续处理;如果为空,则通过getSingleton(String beanName, ObjectFactory<?) singletonFactory方法创建bean实例并进行后续处理。
从三级缓存中获取bean实例的源码如下:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {public Object getSingleton(String beanName) {return getSingleton(beanName, true);}// 这里是经典的DCL(双重检查锁)protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 从一级缓存中获取beanObject singletonObject = this.singletonObjects.get(beanName);// 一级缓存中的bean为空,且当前bean正在创建if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 加锁synchronized (this.singletonObjects) {// 从二级缓存中获取beansingletonObject = this.earlySingletonObjects.get(beanName);// 二级缓存中的bean为空,且允许提前创建if (singletonObject == null && allowEarlyReference) {// 从三级缓存中获取对应的ObjectFactoryObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);// ObjectFactory不为空if (singletonFactory != null) {// 从单例工厂中获取beansingletonObject = singletonFactory.getObject();// 添加到二级缓存this.earlySingletonObjects.put(beanName, singletonObject);// 从三级缓存中删除this.singletonFactories.remove(beanName);}}}}return singletonObject;}...}
主要流程:
- 首先,尝试从一级缓存中
singletonObjects中获取单例Bean - 获取不到,则从二级缓存
earlySingletonObjects中获取单例Bean - 获取不到,则从三级缓存
singletonFactories中获取单例ObjectFactory - 如果从三级缓存中获取成功,则将
ObjectFactory中的object取出放入到二级缓存中,并将ObjectFactory从三级缓存中移除
如果通过三级缓存的查找都没有找到目标bean实例,则通过getSingleton(String beanName, ObjectFactory<?> singleton)方法创建:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {...// 添加互斥锁synchronized (this.singletonObjects) {// 从一级缓存获取Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {// 为空则继续...// 将当前bean名称添加到正在创建bean的集合singletonsCurrentlyInCreationbeforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);...try {// 通过函数式接口创建bean实例,该实例已经经历实例化、属性填充、初始化和后置处理,可直接使用singletonObject = singletonFactory.getObject();newSingleton = true;}catch (IllegalStateException ex) {// 判断在此期间是否隐式出现了单例对象...}finally {// 将当前bean名称从正在创建的集合singletonsCurrentlyInCreation中移除afterSingletonCreation(beanName);}if (newSingleton) {// 添加到缓存中addSingleton(beanName, singletonObject);}}return singletonObject;}}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);}}
重点关注singletonFactory.getObject(),singletonFactory是一个函数式接口,对应AbstractBeanFactory的doGetBean方法中的lambda表达式:
sharedInstance = getSingleton(beanName, () -> {try {// 创建bean实例return createBean(beanName, mbd, args);}catch (BeansException ex) {destroySingleton(beanName);throw ex;}});
重点关注createBean方法,该方法为抽象方法,由AbstractBeanFactory子类AbstractAutowireCapableBeanFactory实现:
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {try {Object beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isTraceEnabled()) {logger.trace("Finished creating instance of bean '" + beanName + "'");}return beanInstance;}}protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// 实例化beanBeanWrapper instanceWrapper = null;...Object bean = instanceWrapper.getWrappedInstance();...synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {// 执行MergedBeanDefinitionPostProcessor类型后置处理器applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}...mbd.postProcessed = true;}}// 如果该bean是单例,并且允许循环依赖的出现以及该bean正在创建中// 那么就标识允许单例bean提前暴露原始对象引用boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {// 添加到单实例集合中,即三级缓存对象,该方法第二个参数类型为ObjectFactory函数式接口addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// 初始化beanObject exposedObject = bean;try {// 属性赋值populateBean(beanName, mbd, instanceWrapper);exposedObject = initializeBean(beanName, exposedObject, mbd);}...// 允许bean提前暴露if (earlySingletonExposure) {// 第二个参数为false标识仅从一级和二级缓存中获取bean单例Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {// 如果从一级和二级缓存中获取到bean实例为空exposedObject = earlySingletonReference;}...}}// Register bean as disposable.try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}...return exposedObject;}
其中addSingletonFactory方法在父类DefaultSingletonBeanRegistry已实现:
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {// 添加互斥锁synchronized (this.singletonObjects) {// 如果一级缓存没有,那么添加到三级缓存if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}}
整个流程总结如下:
面试逼问
(1)只用一级缓存是否可以解决循环依赖?
可以解决。
对于一级缓存,我们不等对象初始化完成之后再存入缓存,而是等对象实例化完成后就提前暴露,存入一级缓存,由于此时缓存对象并没有进行初始化操作,即早期对象。那么A实例化完成提前暴露早期对象,A在属性注入时,发现B不在容器中,也没有提前暴露,那么去进行B的生命周期,B实例化后进行属性注入,发现A提前暴露,那么B可以从一级缓存中获取到A的早期对象完成初始化,回到A的生命周期从而A也可以完成初始化。 但是这样会引发另一个问题:早期对象和完整对象都存在一级缓存中,如果此时来了其他线程并发获取bean,就可能从一级缓存中获取到bean的早期对象,这样明显不行,那么我们不得已在从一级缓存获取对象处加一个互斥锁,以避免这个问题。 加互斥锁代理来另一个问题:容器刷新完成后的普通获取bean的请求都需要竞争锁,如果这样处理,在高并发场景下使用Spring的性能必然降低。
(2)加上二级缓存有什么好处?
优化一级循环加锁带来的性能问题。
将对象的早期对象存入二级缓存中,一级缓存用于存储完整对象。 这样B在创建过程中进行属性注入时,先从一级缓存中获取A,获取失败则从二级缓存中获取。 这种获取bean的逻辑也可能出现其他线程获取到早期对象的问题,所以还是要加互斥锁。只不过这里的加锁逻辑可以下沉到二级缓存。那么普通的getBean请求可以直接从一级缓存获取对象,而不用去竞争锁。
(3)二级缓存如何解决AOP问题?
代理对象提前创建。
Spring支持以CGLIB和JDK动态代理的方式为对象创建代理类以提供AOP支持,代理对象的创建通常是在bean初始化完成之后进行(通过BeanPostProcessor后置处理器)。 如果没有循环依赖,那么代理对象依然在初始化完成后创建,如果有循环依赖,那么提前创建代理对象。 如何判断发生了循环依赖? 在B创建的过程中获取A的时候,发现二级缓存中有A,就说明发生了循环依赖,此时就为A创建代理对象,将其覆盖到二级缓存中,并且将代理对象复制给B的对应数字。 当出现多级循环依赖的时候,可以在对象实例化完成之后,将beanName存在一个Set中,标识对应的bean正在创建中,而当其他对象创建的过程依赖某个对象的时候,判断其是否在这个Set中,如果在就说明发生了循环依赖。
(4)那么三级缓存有什么作用?
虽然仅仅依靠二级缓存能够解决循环依赖和AOP的问题,但是从解决方案上来看,维护代理对象的逻辑和getBean的逻辑过于耦合。
三级缓存的key还是为beanName,但是value是一个函数(ObjectFactory#getBean方法),在该函数中执行获取早期对象的逻辑:getEarlyBeanReference方法。 在getEarlyBeanReference方法中,Spring会调用所有SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法,通过该方法可以修改早期对象的属性或者替换早期对象。这个是Spring留给开发者的另一个扩展点,虽然我们很少使用,不过在循环依赖遇到AOP的时候,代理对象就是通过这个后置处理器创建。
参考资料
[1] 🕊️ 鸟叔博客:https://mrbird.cc/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Spring%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96.html
[2] 💤 黑哥知乎:https://zhuanlan.zhihu.com/p/375308988
