spring系列16:循环依赖

前言

上篇介绍了 Spring 是如何完成自动装配,但是,如果程序员这样配置 Bean

A.java

package com.playwin.bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class A {

   @Autowired
   private B b;

}

B.java

package com.playwin.bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class B {

   @Autowired
   A a;
}

A Bean 需要注入 B Bean,而 B Bean 中需要注入 A Bean

按照上篇介绍,Spring 在 A 实例化后,开始属性注入,如果这个属性也是一个 Bean,会提前实例化。但是,B Bean 实例化也是调用 getBean 方法,同样会走到这里,难道又去实例化 A ?,然后有去实例化 B,然后 ....。

Spring 在实例化一个 Bean 前,不是会检查缓存吗,不会 把 A 存到缓存中吗?那么问题来了,在 A 注入属性 B 的时候,A 这时候是没实例化完成的,不是一个完整的 Bean,这样的半成品的 A 就放到 B 里面,还有可能 A 需要被代理,最后得到的 A,和从 B Bean 属性中的 A 不是同一个对象,这岂不是很乌龙。看来只有看源码才会得到答案。

回顾

在看源码前,先回顾一下前几篇的几处细节。

singletonsCurrentlyInCreation

在 Spring 初始化前,会调用 beforeSingletonCreation(beanName);

protected void beforeSingletonCreation(String beanName) {
   // 在这正在实例化 Bean 会被加入到 singletonsCurrentlyInCreation 集合
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
   }
}

把将要实例化的 Bean 添加到 singletonsCurrentlyInCreation 表示这个 Bean 正在创建,记住这一步很重要。

beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
   this.suppressedExceptions = new LinkedHashSet<>();
}
try {
   //sharedInstance = getSingleton(beanName, () -> {
   // try {
   //    return createBean(beanName, mbd, args);
   // }
   //    catch (BeansException ex) {
   //       ....
   //    }
   // }
   //);
    // 返回完整的 Bean
   singletonObject = singletonFactory.getObject();
   newSingleton = true;
}
catch (IllegalStateException ex) {

}finally {

    // 移出正在创建 Bean 集合
    afterSingletonCreation(beanName);
}


protected void afterSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
        throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
    }
}

在实例化完成之后,包括属性注入,代理等,才会移除该集合。

循环依赖

这次源码可能比较跳,不是按顺序看下来,会标记好方法的。

暴露工厂

//sharedInstance = getSingleton(beanName, () -> {
// try {
//    return createBean(beanName, mbd, args);
// }
//    catch (BeansException ex) {
//       ....
//    }
// }
//);
// 返回完整的 Bean
singletonObject = singletonFactory.getObject();

这行代码会到 doCreateBean 方法

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;

   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }

   if (instanceWrapper == null) {
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   final Object bean = instanceWrapper.getWrappedInstance();

    // 删除代码
    ....

    // Eagerly cache singletons to be able to resolve circular references
    // even when triggered by lifecycle interfaces like BeanFactoryAware.
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        if (logger.isTraceEnabled()) {
            logger.trace("Eagerly caching bean '" + beanName +
                         "' to allow for resolving potential circular references");
        }
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

   Object exposedObject = bean;
    try {

        populateBean(beanName, mbd, instanceWrapper);

        // 删除代码
        ....
    }

   // 删除代码
   ....

   return exposedObject;
}

有两行注释非常明显

Eagerly cache singletons to be able to resolve circular references even when triggered by lifecycle interfaces like BeanFactoryAware.

circular references 循环引用,说明这下面代码与循环依赖有关。

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));


public boolean isSingletonCurrentlyInCreation(String beanName) {
    return this.singletonsCurrentlyInCreation.contains(beanName);
}

BeanDefinition 是否是单例,this.allowCircularReferences

/** Whether to automatically try to resolve circular references between beans. */
private boolean allowCircularReferences = true;

默认为 true。以及是否包含在 SingletonCurrentlyInCreation 集合中。这个 Bean 到这里仅仅是一个对象,并没有注入属性等一系列操作,所以它还在这个集合中。也就是一般情况下,这个变量 earlySingletonExposure 都是为 true

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);
        }
    }
}

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));执行这行代码,Spring 把这个刚刚实例化成对象的 Bean 包装成了一个 singletonFactory 放到了 singletonFactories 集合中。

如果你到这还看不懂 Spring 为什么这么做,继续往下看。

注入 singletonFactorie

A 在执行这之后,会去注入属性,也就是上面配置 B,调用 getBean 方法,B 同样走到这里,开始注入 A,同样是调用 getBean 方法,进入 doGetBean

@Override
public Object getBean(String name) throws BeansException {
   return doGetBean(name, null, null, false);
}


protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                          @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

    // 表面意思转化 BeanName
    // 把别名转为标准 BeanName
    // 有关 FactoryBean BeanName 操作
    final String beanName = transformedBeanName(name);
    Object bean;

    // Eagerly check singleton cache for manually registered singletons.
    // 从缓存中取出 Bean,BeanDefinition 实例化后都会保存到对应的缓存中
    Object sharedInstance = getSingleton(beanName);

    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.trace("....");
            }
            else {
                logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    // BeanDefinition 未实例化,走下面逻辑
    else {

        // 删除代码
        .....
    }

    return (T) bean;
}

doGetBean 最开始有一步重要的操作,从缓存中查找 Bean

Object sharedInstance = getSingleton(beanName);

public Object getSingleton(String beanName) {
    return getSingleton(beanName, true);
}


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 集合 singletonObjects 里拿,这时的 A 没有实例化好,还在属性注入,所以这里的 singletonObject 是空的,再判断 isSingletonCurrentlyInCreation(beanName)这个 Bean 是否是正在创建,这时 A 也是在里面的,if成立,进入代码块,singletonObject = this.earlySingletonObjects.get(beanName);在 earlySingletonObjects 集合中取出来,前面在 doCreateBean 方法时缓存包装 A 对象的工厂,A 就已经从这个集合移出了,allowEarlyReference 传进来是trueSpring 写死了。ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);拿出前面在 doCreateBean 方法时缓存包装 A 对象的工厂, singletonObject = singletonFactory.getObject();再从工厂里拿出 A,返回一个早期 A 代理引用对象。

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

总结

在单例 Bean 实例化的时候,Spring 会先暴露一个包装 A 对象的工厂,如果需要循环依赖,会从工厂中拿出 A 的引用对象,这样 A 注入完 B,引用对象也会完成。

为什么说 Spring 默认支持循环依赖?

有两个在循环依赖关键点判断的时候,allowEarlyReference 和 allowCircularReferences 属性 Spring 默认都是为 true

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值