全方位解析Spring IoC:(九)源码分析——Bean的循环依赖

由于掘金文章存在字数限制,本文拆开了各个章节分开发布,全文可点击以下链接进行阅读:blog.omghaowan.com/archives/sp...

Spring的使用中常常会有bean循环依赖的发生,貌似给人感觉就是这个概念是Spring特有的,但实际上在软件工程中的反模式(anti-pattern)中早已将臭名昭著的循循环依赖原则(ADP)登记在册了,只不过在Spring依赖注入的特性加持下循环依赖变得显而易见了,这也是大部分IoC框架所能遇见的问题。

循环依赖的发生

简单来说,循环依赖即是指模块间的属性存在相互依赖的关系。典型地,在Spring中我们可以轻松地找到以下触发到循环依赖的场景:

  • 构造器自动装配发生循环依赖

    java 复制代码
    @Service
    public class ServiceOne {
    
        private ServiceTwo serviceTwo;
    
        @Autowired
        public ServiceOne(ServiceTwo serviceTwo){
            this.serviceTwo = serviceTwo;
        }
    }
    
    @Service
    public class ServiceTwo {
    
        private ServiceOne serviceOne;
    
        @Autowired
        public ServiceTwo(ServiceOne serviceOne){
            this.serviceOne = serviceOne;
        }
    }
  • 工厂方法自动装配发生循环依赖

    java 复制代码
    @Bean
    public BeanA beanA(BeanB beanB) {
        return new BeanA();
    }
    
    @Bean
    public BeanB BeanB(BeanA beanA) {
        return new BeanB();
    }
    
    public class BeanA {
    }
    
    public class BeanB {
    }
  • Setter方法自动装配发生循环依赖

    java 复制代码
    @Service
    public class ServiceOne {
    
        private ServiceTwo serviceTwo;
    
        @Autowired
        public void setServiceTwo(ServiceTwo serviceTwo){
            this.serviceTwo = serviceTwo;
        }
    }
    
    @Service
    public class ServiceTwo {
    
        private ServiceOne serviceOne;
    
        @Autowired
        public void setServiceOne(ServiceOne serviceOne){
            this.serviceOne = serviceOne;
        }
    }
  • 字段自动装配发生循环依赖

    java 复制代码
    @Service
    public class ServiceOne {
        @Autowired
        private ServiceTwo serviceTwo;
    }
    
    @Service
    public class ServiceTwo {
        @Autowired
        private ServiceOne serviceOne;
    }

其中,对于上述某些场景在发生循环依赖时可能会被Spring顺利解决而避免异常的发生;对于另一些场景发生循环依赖时则可能无法被Spring解决而抛出循环依赖异常。为了避免在使用Spring的过程中由于使用不当触发到循环依赖异常,下面我们从Spring对循环依赖的的解决方案入手在原理上认识到如何规避循环依赖异常的发生。

循环依赖的解决

在通过AbstractBeanFactory#doGetBean方法执行bean的实例化的时候,Spring会在其中夹杂着对循环依赖的处理,以此来尽可能地避免在实例化过程中循环依赖异常的发生。那下面我们将从AbstractBeanFactory#doGetBean方法开始对Spring循环依赖的解决思路进行分析。

为了更清晰的阐述,这里将bean的创建分为单例模式和多例模式2种:

  1. 单例模式(Singleton)的bean创建。
  2. 多例模式(Prototype)的bean创建。

由于单例模式和多例模式下对循环依赖的处理方式基本涵盖了所有场景,我们完全可以结合单例模式和多例模式下对循环依赖的处理方式来完成对其他作用域的处理分析,因此在这里就不对Spring其他类型的作用域展示分析了。

单例模式的bean创建

在单例模式下,doGetBean方法首先会逐个从不同的缓存级别中获取bean实例,如果其中某个层级不为空则将其取出并进一步处理后返回;而如果各个层级都不存在则表示当前bean尚未被实例化,因此对该bean执行实例化操作。下面,我们看到具体的代码步骤:

注意,在此章节贴出的代码并非完整代码,而是经过笔者简化后仅作用于实例化单例作用域bean的代码。

java 复制代码
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws ... {

    String beanName = ...;

    Object beanInstance;

    // Eagerly check singleton cache for manually registered singletons.
    Object sharedInstance = getSingleton(beanName, true);
    if (sharedInstance != null && args == null) {
        // ...
        beanInstance = getObjectForBeanInstance(sharedInstance, ...);
    }

    else {

        // ...
        
        RootBeanDefinition mbd = ...; 

        // ...

        // Create bean instance.
        sharedInstance = getSingleton(beanName, () -> {
            // 假设没有异常
            return createBean(beanName, mbd, args);
        });
        beanInstance = getObjectForBeanInstance(sharedInstance, ...);
        
    }

    return adaptBeanInstance(name, beanInstance, requiredType);
}

doGetBean方法中,首先会使用getSingleton(String, boolean)方法在缓存中查找出对应的bean实例,在查找失败的情况下再使用getSingleton(String, ObjectFactory)方法创建对应的bean实例。虽然这么一看其执行流程与常规的缓存策略大致相同,但实际上它会在常规缓存策略的基础上添加了多级缓存策略和构建重复性验证来避免循环依赖的发生。

在进行具体的执行流程之前,我们先来对Spring在单例bean的实例化过程中涉及到的多级缓存策略提前进行梳理。具体地,bean在实例化过程中为保证单例作用域的特性、循环依赖的处理和一些设计理念的考虑一共使用到了3个级别的缓存,即:

缓存 等级 说明
singletonObjects 三级缓存 缓存已完成的bean实例,已执行完成bean的初始化。
earlySingletonObjects 二级缓存 缓存构建初期的bean实例,尚未执行bean实例的初始化。
singletonFactories 一级缓存 缓存构建bean的工厂实例,尚未执行bean实例的创建。

根据上述对每个级别缓存的阐述,基本可以分析出:

  • 在三级缓存singletonObjects的作用下保证了bean单例作用域的特性;
  • 在二级缓存earlySingletonObjects的作用下完成了循环依赖的解决,这其实与常规的循环依赖解决方案大致相同,即通过缓存将尚未执行初始化的对象提前暴露出去;
  • 在一级缓存singletonFactories的作用下可以延迟二级缓存中earlySingletonObject对象的生成(只有在循环依赖真正发生时才特意生成出earlySingletonObject对象),毕竟循环依赖在bean的引用关系中并不一定会发生(Spring也不推荐在bean的引用关系中发生循环依赖)。

当然,对各级缓存作用的分析仅是基于它们表面的代码行为,而对其在Spring中真正发挥到的作用还需要进一步阅读doGetBean方法的执行逻辑。那下面我们就开始展开对doGetBean方法的阅读理解与逻辑分析。

doGetBean方法的执行流程中,Spring首先会通过getSingleton(String, boolean)方法从bean的多级缓存中查询缓存实例:

java 复制代码
/**
 * Return the (raw) singleton object registered under the given name.
 * <p>Checks already instantiated singletons and also allows for an early
 * reference to a currently created singleton (resolving a circular reference).
 * @param beanName the name of the bean to look for
 * @param allowEarlyReference whether early references should be created or not
 * @return the registered singleton object, or {@code null} if none found
 */
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // Quick check for existing instance without full singleton lock
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            // 忽略并发情况下的获取: Consistent creation of early reference within full singleton lock
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
                singletonObject = singletonFactory.getObject();
                this.earlySingletonObjects.put(beanName, singletonObject);
                this.singletonFactories.remove(beanName);
            }
        }
    }
    return singletonObject;
}

其中,getSingleton(String, boolean)方法正如上述所说会逐级查询缓存实例,若查询成功则在经过相应的处理后返回。具体地,getSingleton会通过第2个参数allowEarlyReference来控制是否执行一级缓存singletonFactories的查询与二级缓存earlySingletonObject的创建。下面,我们将根据allowEarlyReference参数不同的值总结出不同的执行流程:

  • allowEarlyReference参数为true,允许执行一级缓存singletonFactories的查询与二级缓存earlySingletonObject的创建:

    1. 从三级缓存中查询已执行初始化完成的bean实例singletonObject,如果查询存在直接返回(保证了bean单例作用域的特性),否则执行第2步。
    2. 从二级缓存中查询构建初期尚未执行初始化的bean实例earlySingletonObject,如果查询存在直接返回(证实了bean发生过循环依赖),否则执行第3步。
    3. 从一级缓存中查询是否存在构建bean的工厂实例singletonFactory,如果不存在直接返回null(证实了bean尚未被创建),否则:
      1. 通过singletonFactory实例创建出earlySingletonObject实例。
      2. earlySingletonObject实例添加到二级缓存中。
      3. singletonFactory实例从一级缓存中移除。
      4. earlySingletonObject实例返回。

    即,getSingleton(String, true)方法会分别从三级缓存singletonObjects、二级缓存earlySingletonObjects到一级缓存singletonFactorys中查询对应的缓存实例,如果在三级缓存、二级缓存中查询成功则直接返回;如果在一级缓存中查询成功则根据工厂实例创建出earlySingletonObject实例添加到二级缓存并返回该实例;如果都没有查询成功则返回null表示尚未开始bean实例的创建。

    text 复制代码
                                    +
                                    |
                                    |
                                    v
                          +---------+----------+
                          |                    |   success
                          |  singletonObjects  +------------>+
                          |                    |             |
                          +---------+----------+             |
                                    |                        |
                                    |                        |
                                    | failure                |
                                    |                        |
                                    v                        |
                        +-----------+------------+           |
                        |                        |  success  |
            +---------->+ earlySingletonObjects  +---------->+
            |           |                        |           |
            |           +-----------+------------+           |
            |                       |                        |
            |                       |                        |
    success |                       | failure                |
            |                       |                        |
            |                       v                        |
            |            +----------+-----------+            |
            |            |                      |            |
            +------------+  singletonFactories  |            |
                         |                      |            |
                         +----------+-----------+            |
                                    |                        |
                                    |                        |
                                    | failure                |
                                    |                        |
                                    v                        v
                                   null       singletonObject/earlySingletonObject
  • allowEarlyReference参数为false,不允许执行一级缓存singletonFactories的查询与二级缓存earlySingletonObject的创建:

    1. 从三级缓存中查询已执行初始化完成的bean实例singletonObject,如果查询存在直接返回(保证了bean单例作用域的特性),否则执行第2步。
    2. 从二级缓存中查询构建初期尚未执行初始化的bean实例earlySingletonObject,如果查询存在直接返回(证实了bean发生循环依赖),否则直接返回null

    即,getSingleton(String, false)方法会分别从三级缓存singletonObjects和二级缓存earlySingletonObjects中查询对应的缓存实例,如果在三级缓存或者二级缓存中查询成功则直接返回,否则返回null

    text 复制代码
                +
                |
                |
                v
      +---------+----------+
      |                    |   success
      |  singletonObjects  +------------>+
      |                    |             |
      +---------+----------+             |
                |                        |
                |                        |
                | failure                |
                |                        |
                v                        |
    +-----------+------------+           |
    |                        |  success  |
    | earlySingletonObjects  +---------->+
    |                        |           |
    +-----------+------------+           |
                |                        |
                |                        |
                | failure                |
                |                        |
                v                        v
               null       singletonObject/earlySingletonObject    

在代码中我们可以看到doGetBean方法执行的是getSingleton(String, true)方法,即会分别从三级缓存singletonObjects、二级缓存earlySingletonObjects到一级缓存singletonFactorys中查询对应的缓存实例。为了能完整地描述循环依赖的整个流程,这里就暂且假设bean尚未被创建过。因此,Spring在执行getSingleton(String, true)方法时返回null,随即进入到getSingleton(String, ObjectFactory)方法执行bean实例的创建,具体如下所示:

java 复制代码
/**
 * Return the (raw) singleton object registered under the given name,
 * creating and registering a new one if none registered yet.
 * @param beanName the name of the bean
 * @param singletonFactory the ObjectFactory to lazily create the singleton
 * with, if necessary
 * @return the registered singleton object
 */
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
        // ...
        beforeSingletonCreation(beanName);
        boolean newSingleton = false;
        try {
            // 假设无异常
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
        } finally {
            afterSingletonCreation(beanName);
        }
        if (newSingleton) {
            addSingleton(beanName, singletonObject);
        }
    }
    return singletonObject;
}

如同常规单例模式下的实例创建流程,在getSingleton(String, ObjectFactory)方法中也会优先从三级缓存singletonObjects中获取实例,在获取失败后才会执行singletonObject对象的创建。而在singletonObject的创建流程中,除了会完成singletonObject实例的创建,还添加了实例创建的前置处理beforeSingletonCreation与后置处理afterSingletonCreation,具体代码如下所示:

java 复制代码
/** Names of beans that are currently in creation. */
private final Set<String> singletonsCurrentlyInCreation =
        Collections.newSetFromMap(new ConcurrentHashMap<>(16));

/**
 * Callback before singleton creation.
 * <p>The default implementation register the singleton as currently in creation.
 * @param beanName the name of the singleton about to be created
 * @see #isSingletonCurrentlyInCreation
 */
protected void beforeSingletonCreation(String beanName) {
    if (... && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
}

/**
 * Callback after singleton creation.
 * <p>The default implementation marks the singleton as not in creation anymore.
 * @param beanName the name of the singleton that has been created
 * @see #isSingletonCurrentlyInCreation
 */
protected void afterSingletonCreation(String beanName) {
    if (... && !this.singletonsCurrentlyInCreation.remove(beanName)) {
        throw new IllegalStateException("...");
    }
}

在前置处理beforeSingletonCreation(创建bean实例前)中,Spring会尝试将当前beanName加入到singletonsCurrentlyInCreation中,若加入失败则抛出异常BeanCurrentlyInCreationException;而后置处理afterSingletonCreation(创建bean实例后)中,Spring会尝试将当前beanNamesingletonsCurrentlyInCreation中移除,若移除失败则抛出异常IllegalStateException

实际上,Spring就是通过这种前置处理和后置处理来判断bean在实例化过程中是否发生了无法解决的循环依赖,进而在请求时抛出异常提前阻止程序进入无限循环。

典型地,当Spring在构造器自动装配时发生了循环依赖即会在前置处理中触发BeanCurrentlyInCreationException异常的发生,具体流程如下图所示:

text 复制代码
                           +
                           |
                           |
                           v
             +-------------+-------------+
   throw ex  |                           |   circular references
<------------+  beforeSingletonCreation  +<----------------------+
             |                           |                       |
             +-------------+-------------+                       |
                           |                                     |
                           |                                     |
                           |                                     |
                           v                                     |
            +--------------+---------------+          +----------+-----------+
            |                              |          |                      |
            |  singletonFactory#getObject  +--------->+  createBeanInstance  |
            |                              |          |                      |
            +--------------+---------------+          +----------+-----------+
                           |                                     |
                           |                                     | To expose earlySingletonObject
                           |                                     |
                           v                                     v
              +------------+-------------+
              |                          |
              |  afterSingletonCreation  |
              |                          |
              +--------------------------+

回归到正常流程上,如果在前置处理beforeSingletonCreation没有发现循环依赖,那么Spring就会进入到createBean方法执行bean实例的创建。

java 复制代码
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {

    RootBeanDefinition mbdToUse = mbd;

    //...

    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    return beanInstance;
}

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

    // 1. 执行`raw bean`实例的创建
    BeanWrapper instanceWrapper = ...createBeanInstance(beanName, mbd, args);
    Object bean = instanceWrapper.getWrappedInstance();

    // ...       

    // 2. 通过`raw bean`实例构建工厂实例并将其添加到一级缓存
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

    Object exposedObject = bean;
    // 3. 执行`raw bean`实例的属性填充
    populateBean(beanName, mbd, instanceWrapper);
    // 4. 执行`raw bean`实例的初始化
    exposedObject = initializeBean(beanName, exposedObject, mbd);

    // 5. 归正最终返回的`bean`实例
    Object earlySingletonReference = getSingleton(beanName, false);
    if (earlySingletonReference != null) {
        if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
        }
        else if (...) {
            ...
        }
    }
    
    // ...

    return exposedObject;
}

createBean方法中不但会执行基本的bean实例化流程,而且还会额外添加解决循环依赖的代码块,具体执行流程如下所示:

  1. 执行raw bean实例的创建
  2. 通过raw bean实例构建工厂实例并将其添加到一级缓存
  3. 执行raw bean实例的属性填充
  4. 执行raw bean实例的初始化
  5. 归正最终返回的bean实例

其中,步骤134都是bean实例化流程的基本步骤,这在上文《源码分析:Bean的创建》章节中已有所提及,这里就不在赘述了。而步骤2和步骤5则主要用于解决bean的循环依赖,下面我们来详细看看这2个步骤:

根据上述流程,Spring在执行完bean基本对象的创建后就会执行步骤2(在属性填充和实例初始化前),即通过raw bean实例构造出工厂实例并将其添加到一级缓存:

java 复制代码
// 视`() -> getEarlyBeanReference(beanName, mbd, bean)`为工厂实例
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

/**
 * Add the given singleton factory for building the specified singleton
 * if necessary.
 * <p>To be called for eager registration of singletons, e.g. to be able to
 * resolve circular references.
 * @param beanName the name of the bean
 * @param singletonFactory the factory for the singleton object
 */
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    if (!this.singletonObjects.containsKey(beanName)) {
        this.singletonFactories.put(beanName, singletonFactory);
        this.earlySingletonObjects.remove(beanName);
    }
}

通过这种方式,如果Spring在属性填充阶段发生了循环依赖再次向doGetBean方法请求当前bean时就会触发到getSingleton(String, true)方法的执行(此时在一级缓存中会存在当前bean的工厂实例缓存)。在getSingleton(String, true)方法中会一路探查到一级缓存将当前存储的工厂实例取出执行(即执行getEarlyBeanReference方法),具体执行逻辑如下所示:

java 复制代码
/**
 * Obtain a reference for early access to the specified bean,
 * typically for the purpose of resolving a circular reference.
 * @param beanName the name of the bean (for error handling purposes)
 * @param mbd the merged bean definition for the bean
 * @param bean the raw bean instance
 * @return the object to expose as bean reference
 */
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    //...假设符合条件
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
            SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
        }
    }
    return exposedObject;
}

public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
    /**
     * Obtain a reference for early access to the specified bean,
     * typically for the purpose of resolving a circular reference.
     * <p>This callback gives post-processors a chance to expose a wrapper
     * early - that is, before the target bean instance is fully initialized.
     * The exposed object should be equivalent to the what
     * {@link #postProcessBeforeInitialization} / {@link #postProcessAfterInitialization}
     * would expose otherwise. Note that the object returned by this method will
     * be used as bean reference unless the post-processor returns a different
     * wrapper from said post-process callbacks. In other words: Those post-process
     * callbacks may either eventually expose the same reference or alternatively
     * return the raw bean instance from those subsequent callbacks (if the wrapper
     * for the affected bean has been built for a call to this method already,
     * it will be exposes as final bean reference by default).
     * <p>The default implementation returns the given {@code bean} as-is.
     * @param bean the raw bean instance
     * @param beanName the name of the bean
     * @return the object to expose as bean reference
     * (typically with the passed-in bean instance as default)
     */
    default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

getEarlyBeanReference方法中会遍历所有的SmartInstantiationAwareBeanPostProcessorraw bean进行修饰处理,默认情况下SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference不会做任何处理直接原样返回bean,而如果Spring使用了AOP特性则会通过其实现类中的AbstractAutoProxyCreator#getEarlyBeanReference方法对bean包装一层代理,具体如下所示:

java 复制代码
/**
 * {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
 * that wraps each eligible bean with an AOP proxy, delegating to specified interceptors
 * before invoking the bean itself.
 * ...
 */
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, ... {

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 特别需要关注这里,在对bean包装前会将bean存入缓存中
        this.earlyProxyReferences.put(cacheKey, bean);
        return wrapIfNecessary(bean, beanName, cacheKey);
    }


    /**
     * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
     * @param bean the raw bean instance
     * @param beanName the name of the bean
     * @param cacheKey the cache key for metadata access
     * @return a proxy wrapping the bean, or the raw bean instance as-is
     */
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {       
        // ...
        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }
        // ...
        return bean;
    }
}

也就是说,在默认情况下如果没有使用Spring AOPSmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference会原样返回bean实例;而如果使用了Spring AOPSmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference则会包装一层代理层再返回。而不管在哪种情况下执行,getSingleton(String, true)方法在通过getEarlyBeanReference方法获得earlySingletonObject实例后就将它添加到二级缓存中,并最终将earlySingletonObject实例返回,完成对早期bean的提前暴露,从而终止对bean的再次创建、属性填充和实例初始化,避免程序在循环依赖中不断地对bean进行实例化的困境。Spring就是通过这种方式来解决bean的循环依赖的,具体流程如下图所示:

text 复制代码
                              +
                              | get A
                              v
                      +-------+--------+
             +--------+  getSingleton  +<-----------------------------------+
             |        +-------+--------+                                    |
             |                |                                             |
             |                v                                             |
             |     +----------+-----------+   +--------------+   +-------+  |
             |     |  createBeanInstance  +-->+ populateBean +-->+  |-|  +--->
             |     +----------------------+   +------+-------+   +-------+  |
             |                                       | get B                | get A
             v                                       v                      |
 +-----------+-----------+                   +-------+--------+             |
 |  getSingletonFactory  |                   |  getSingleton  |             |
 +-----------+-----------+                   +-------+--------+             |
             |                                       |                      |
             v                                       v                      |
+------------+------------+               +----------+-----------+   +------+-------+   +-------+
|  getEarlyBeanReference  |               |  createBeanInstance  +-->+ populateBean +-->+  |-|  +--->
+------------+------------+               +----------------------+   +------+-------+   +-------+
             |                                                              ^
             |                                                              |
             +--------------------------------------------------------------+

一般来说,getEarlyBeanReference方法返回的对象与postProcessBeforeInitialization/postProcessAfterInitialization所返回的对象是相等的。不过,Spring并没有特意对此作强制要求,如果我们在postProcessBeforeInitialization/postProcessAfterInitialization中返回与之前不一样的引用就会造成它们之间的等式不成立。

再结合上文bean创建流程中前置处理beforeSingletonCreation方法与后置处理afterSingletonCreation方法的判断,不难得出Spring就是通过这种将早期bean提前暴露到缓存的方式来避免bean依赖的创建再次进入到前置处理beforeSingletonCreation方法中造成异常的抛出,具体可参考以下流程图:

text 复制代码
                        |
                        |
                        v 
               +--------+-------+
               |                |              circular references
+--------------+  getSingleton  +<--------------------------------------------------+
|              |                |                                                   |
|              +--------+-------+                                                   |
|                       |                                                           |
|              create if not exists                                                 |
|                       |                                                           |
|     +------------------------------------+                                        |
|     |                 |                  |                                        |
|     |                 v                  |                                        |
|     |   +-------------+-------------+    |                                        |
|     |   |                           |    |                                        |
|     |   |  beforeSingletonCreation  |    |                                        |
|     |   |                           |    |                                        |
|     |   +-------------+-------------+    |                                        |
|     |                 |                  |                                        |
|     |                 |                  |                                        |
|     |                 |                  |                                        |
|     |                 v                  |                                        |
|     |  +--------------+---------------+  |       +----------------------+         |
|     |  |                              |  |       |                      |         |
|     |  |  singletonFactory#getObject  +--------->+  createBeanInstance  |         |
|     |  |                              |  |       |                      |         |
|     |  +--------------+---------------+  |       +----------+-----------+         |
|     |                 |                  |                  |                     |
|     |                 |                  |      To expose earlySingletonObject    |
|     |                 |                  |                  |                     |
|     |                 v                  |                  v                     |
|     |    +------------+-------------+    |       +----------+------------+        |
|     |    |                          |    |       |                       |        |
|     |    |  afterSingletonCreation  |    |       |  addSingletonFactory  |        |
|     |    |                          |    |       |                       |        |
|     |    +--------------------------+    |       +----------+------------+        |
|     |                                    |                  |                     |
|     |                                    |                  |                     |
|     +------------------------------------+                  |                     |
|                                                             v                     |
|                                                     +-------+---------+           |
|                                                     |                 |           |
+---------------------------------------------------->+  populateBean   +-----------+
             get from cache if exists                 |                 |
                                                      +-------+---------+
                                                              |
                                                              |continue
                                                              |
                                                              v

简单来说,Spring就是通过将早期bean实例earlySingletonObject提前暴露出去的方式来解决bean的循环依赖。这样一看感觉Spring已经完成bean的循环依赖的处理了,但实际上这里仅仅考虑了没有代理层(特指AbstractAutoProxyCreator,包含Spring AOP的处理)情况下的循环依赖,而对于循环依赖发生在需经过代理层处理(特指AbstractAutoProxyCreator,包含Spring AOP的处理)的bean实例时则要加上步骤5的配合了。

其中,createBean方法步骤5的执行流程如下所示:

  1. 通过getSingleton(String, false)方法获取缓存earlySingletonReference,如果存在则继续执行第2步,否则结束执行。
  2. 通过exposedObject属性判断bean实例初始化前后引用是否发生过变化,如果没发生过变化则继续执行第3步,否则结束执行。
  3. earlySingletonReference赋值给exposedObject属性替换最终暴露的bean实例。

需要注意,getSingleton(String, false)方法是不允许执行一级缓存singletonFactories的查询与二级缓存earlySingletonObject的创建。

可能对于上述步骤5的执行流程看上去会有点迷惑,下面将结合上下文对它进一步解读:

  1. 对于getSingleton(String, false)方法,如果earlySingletonReference为非空则表示发生过循环依赖,否则未发生过。
  2. 对于initializeBean实例初始化,如果其中初始化前置处理或者后置处理发生了类似于添加代理层的操作就会导致引用发生变化,否则不发生。

对于第2点中提及到在实例初始化的前置处理或者后置处理中如果将传入的实例对象变更为另一种对象也会导致引用发生变化,不过由于这种操作并非常规操作(Spring也不推荐),在这里就不往这个方向分析了。

根据对Spring AOP的了解,在使用Spring AOP时会使用动态代理为bean添加一层代理层。按照这种前提显然使用了Spring AOP的情况是不会触发到步骤5的,但对于没有使用Spring AOP的场景就更让人觉得匪夷所思了,因为在这种情况下无论是通过getEarlyBeanReference方法提前暴露的对象还是在经过实例初始化后的对象都是不变的(它们都是原始的bean引用),这样就更没有替换的必要了。在笔者一筹莫展之际,重新阅读了一遍用于实现Spring AOPAbstractAutoProxyCreator类,并且在结合用于提前暴露早期beangetEarlyBeanReference方法和用于实例初始化的postProcessAfterInitialization方法后找到了答案,下面我们先来看看这2个方法:

java 复制代码
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, ... {

    private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 在raw bean进行Spring AOP前将它存入earlyProxyReferences缓存中
        this.earlyProxyReferences.put(cacheKey, bean);
        return wrapIfNecessary(bean, beanName, cacheKey);
    }

    /**
     * Create a proxy with the configured interceptors if the bean is
     * identified as one to proxy by the subclass.
     * @see #getAdvicesAndAdvisorsForBean
     */
    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            // 从earlyProxyReferences缓存中取出raw bean进行判断
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
}

看到这里答案应该呼之欲出了,我们拿到这两个方法代入到doCreateBean方法中就清晰明了了,即:

  1. 执行createBeanInstance方法创建raw bean实例。
  2. 将第1步中的raw bean实例传入addSingletonFactory方法创建出其bean的工厂实例并将其添加到一级缓存中。
  3. 执行populateBean方法给第1步中的raw bean实例填充属性。
    • 在执行期间发生了循环依赖再次执行getSingleton(String, true)方法查询出在第2步中缓存的工厂实例,拿出该工厂实例触发getEarlyBeanReference方法的执行。
      1. 将第2步中传入的raw bean实例添加到earlyProxyReferences缓存。
      2. 将第2步中传入的raw bean实例包装代理层(方法返回,结束getSingleton(String, true)方法的执行(查询成功,结束循环依赖))。
  4. 执行initializeBean方法给第1步中的raw bean实例初始化。
    • AbstractAutoProxyCreator后置处理器(包含Spring AOP的处理)的postProcessAfterInitialization方法中判断传入的bean实例与第3步循环依赖中触发earlyProxyReferences缓存的bean实例相等(都为raw bean实例),跳过执行对传入bean实例(raw bean实例)的代理封装(方法返回)。
  5. 归正最终返回的bean实例
    1. 通过getSingleton(beanName, false)方法获取到earlySingletonReference实例不为空(发生过循环依赖),继续往下执行。
    2. 通过比较exposedObject实例发现没有发生过变化(因为在4步中跳过了bean实例的代理封装),继续往下执行。
    3. exposedObject属性设置为earlySingletonReference,完成将提前暴露的代理对象归正给当前返回的exposedObject属性,保证了循环依赖提前暴露出去的对象与初始化后返回的对象一致。

简单来说,Spring在循环依赖发生时将封装了代理的raw bean提前暴露出去,然后在属性填充和初始化(不包含类似Spring AOP等一系列会让bean引用发生变化的处理)阶段对raw bean内的属性等进行的赋值/修改操作会同时作用于传入bean引用与提前暴露出去包含在代理中的bean引用(两者为同一bean引用),而在初始化阶段中对后置处理器AbstractAutoProxyCreator(包含Spring AOP的处理)的执行会对发生过bean提前暴露的实例不做代理封装并在初始化结束后将提前暴露的代理实例赋值给最终返回的bean实例(替换)。通过这种方式,Spring在最大程度上保证循环依赖提前暴露的出去的对象与在初始化后返回的对象一致,这也呼应了上文提到的:"一般来说,getEarlyBeanReference方法返回的对象与postProcessBeforeInitialization/postProcessAfterInitialization所返回的对象是相等的"。

不难得出,如果bean仅通过AbstractAutoProxyCreator(包含Spring AOP的处理)后置处理器来生成代理层(没有覆盖非protected的方法),无论在初始化阶段中后置处理器按照哪个顺序执行,最终都能保证提前暴露的bean引用与初始化执行后的bean引用一致(通过AbstractAutoProxyCreator来添加代理层可以确保getEarlyBeanReference方法和postProcessAfterInitialization方法都有添加代理层的逻辑,使得最终循环依赖提前暴露的bean引用与初始化执行后的bean引用一致)。

没进行过Spring AOPbean实例也是会执行步骤5(归正最终返回的bean实例)的,但是由于提前暴露的bean引用与初始化后的bean引用并没有发生过变化,因此在这种情况下步骤5是冗余处理。

但如果我们在bean实例化的过程中添加自定义的SmartInstantiationAwareBeanPostProcessor来生成代理层,那最终生成的bean实例很有可能会存在一定的问题。而这其中的问题会根据自定义的SmartInstantiationAwareBeanPostProcessor的执行顺序(在AbstractAutoProxyCreator前后)有所不同,下面我们将根据自定义的SmartInstantiationAwareBeanPostProcessor的执行顺序分点阐述。

首先,假设下面SmartInstantiationAwareBeanPostProcessor类中getEarlyBeanReference方法和postProcessAfterInitialization方法生成代理层的逻辑是相同的,因为在不相同的情况下提前暴露的对象与初始化后所生成的对象必然不一致(无任何探讨的意义)。同时这里也得出一个结论:在实现SmartInstantiationAwareBeanPostProcessor类时必须保证getEarlyBeanReference方法和postProcessAfterInitialization方法中的逻辑是相同的,以此来达到提前暴露的对象与初始化后所生成的对象是一致的。

另外,如果我们使用更顶级的BeanPostProcessor类来生成代理层,那么最终生成的bean实例也极有可能是有问题的。根据上文分析,在发生循环依赖时使用getEarlyBeanReference方法提前暴露bean实例(含代理层)是通过SmartInstantiationAwareBeanPostProcessor类来实现的,如果我们使用BeanPostProcessor类来实现的话就会导致在getEarlyBeanReference方法中暴露的对象没有经过BeanPostProcessor来生成代理层,而在初始化后生成的对象则是经过BeanPostProcessor来生成代理层,这显然会发生bean实例不一致的问题。

  • 当生成代理层的自定义SmartInstantiationAwareBeanPostProcessorAbstractAutoProxyCreator之前:

    text 复制代码
                         +
                         |
                         v
    +--------------------+-----------------------+
    |                                            |
    |  SmartInstantiationAwareBeanPostProcessor  |
    |                                            |
    +--------------------+-----------------------+
                         |
                         v
           +-------------+--------------+
           |                            |
           |  AbstractAutoProxyCreator  |
           |                            |
           +-------------+--------------+
                         |
                         v

    在这种情况下,doCreateBean方法的执行流程就演变为下面这样了:

    1. 执行createBeanInstance方法创建raw bean实例。
    2. 将第1步中的raw bean实例传入addSingletonFactory方法创建出其bean的工厂实例并将其添加到一级缓存中。
    3. 执行populateBean方法给第1步中的raw bean实例填充属性(发生了循环依赖)。
      • 在执行期间发生了循环依赖再次执行getSingleton(String, true)方法查询出在第2步中缓存的工厂实例,拿出该工厂实例触发getEarlyBeanReference方法的执行。
        1. 在自定义SmartInstantiationAwareBeanPostProcessor中为传入bean实例封装一层代理层(方法返回,进入AbstractAutoProxyCreator继续执行)。
        2. AbstractAutoProxyCreator中将传入bean实例添加到earlyProxyReferences缓存中。
        3. AbstractAutoProxyCreator中将传入bean实例封装一层代理层(方法返回,结束getSingleton(String, true)方法的执行(查询成功,结束循环依赖))。
    4. 执行initializeBean方法给第1步中的raw bean实例初始化。
      1. SmartInstantiationAwareBeanPostProcessorpostProcessAfterInitialization方法中为传入bean实例封装一层代理层(方法返回)。
      2. AbstractAutoProxyCreatorpostProcessAfterInitialization方法中判断传入的bean实例与从第2AbstractAutoProxyCreator#earlyProxyReferences缓存中的bean实例并不想等(传入的是在SmartInstantiationAwareBeanPostProcessor#postProcessAfterInitialization中添加了代理层的bean实例,而AbstractAutoProxyCreator#earlyProxyReferences缓存的是第3步中SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference添加了代理层的bean实例,在没有重写等式判断方法和添加缓存的前提下是不相等的),所以接着为传入bean实例的代理封装(方法返回)。
    5. 归正最终返回的bean实例
      1. 通过getSingleton(beanName, false)方法获取到earlySingletonReference实例不为空(发生过循环依赖),继续往下执行。
      2. 通过比较exposedObject实例发现发生过变化,结束执行(不替换)。

    在上述流程中我们可以看到虽然循环依赖提前暴露的对象引用与初始化后获得的对象引用是不相等的,但是它们所实现的代理层和功能点都是相同的。这在常规的场景下一般是不会产生什么大的问题的,因为在外层代理层功能相同的情况下表明了最终所经过的额外逻辑是相同的,而最终委托给被代理对象时所执行的对象也是同一单例作用域下的实例。

  • 当生成代理层的自定义SmartInstantiationAwareBeanPostProcessorAbstractAutoProxyCreator之后:

    text 复制代码
                         +
                         |
                         v
           +-------------+--------------+
           |                            |
           |  AbstractAutoProxyCreator  |
           |                            |
           +-------------+--------------+
                         |
                         v
    +--------------------+-----------------------+
    |                                            |
    |  SmartInstantiationAwareBeanPostProcessor  |
    |                                            |
    +--------------------+-----------------------+
                         |
                         v

    在这种情况下,doCreateBean方法的执行流程就演变为下面这样了:

    1. 执行createBeanInstance方法创建raw bean实例。
    2. 将第1步中的raw bean实例传入addSingletonFactory方法创建出其bean的工厂实例并将其添加到一级缓存中。
    3. 执行populateBean方法给第1步中的raw bean实例填充属性(发生了循环依赖)。
      • 在执行期间发生了循环依赖再次执行getSingleton(String, true)方法查询出在第2步中缓存的工厂实例,拿出该工厂实例触发getEarlyBeanReference方法的执行。
        1. AbstractAutoProxyCreator中将传入bean实例添加到earlyProxyReferences缓存中。
        2. AbstractAutoProxyCreator中将传入bean实例封装一层代理层(方法返回,进入SmartInstantiationAwareBeanPostProcessor继续执行)。
        3. 在自定义SmartInstantiationAwareBeanPostProcessor中为传入bean实例封装一层代理层(方法返回,结束getSingleton(String, true)方法的执行(查询成功,结束循环依赖))。
    4. 执行initializeBean方法给第1步中的raw bean实例初始化。
      1. AbstractAutoProxyCreatorpostProcessAfterInitialization方法中判断传入的bean实例与从第2步中AbstractAutoProxyCreator#earlyProxyReferences缓存中的bean实例相等,跳过执行对传入bean实例的代理封装(方法返回)。
      2. SmartInstantiationAwareBeanPostProcessorpostProcessAfterInitialization方法中为传入bean实例封装一层代理层(方法返回)。
    5. 归正最终返回的bean实例
      1. 通过getSingleton(beanName, false)方法获取到earlySingletonReference实例不为空(发生过循环依赖),继续往下执行。
      2. 通过比较exposedObject实例发现发生过变化,结束执行(不替换)。

    在上述流程中我们可以看到循环依赖提前暴露的对象引用与初始化后获得的对象不但引用是不相等的,而且在功能上也是有所差异,即对循环依赖提前暴露的对象是添加了AbstractAutoProxyCreatorSmartInstantiationAwareBeanPostProcessor两个代理层的,而在初始化后所得到的对象只添加了SmartInstantiationAwareBeanPostProcessor的代理层,这种导致功能缺失的差异显然是致命的。

    因此,我们在开发过程中应该尽可能的保证这种执行顺序的发生。与此同时,为了避免这种情况的发生Spring也通过ProxyProcessorSupportAbstractAutoProxyCreator的执行顺序设置到了最后。

    java 复制代码
    public class ProxyProcessorSupport extends ... implements Ordered, ... {
    
        /**
         * This should run after all other processors, so that it can just add
         * an advisor to existing proxies rather than double-proxy.
         */
        private int order = Ordered.LOWEST_PRECEDENCE;
    
        @Override
        public int getOrder() {
            return this.order;
        }
    
    }

    在开发过程中,我们应该尽量避免将自定义的SmartInstantiationAwareBeanPostProcessor优先级设置为最低LOWEST_PRECEDENCE,因为两个优先级相同的类会继续根据第二维度的顺序执行。

关于Spring在执行bean实例化时为什么是将工厂实例提前暴露出去,而不是直接提前构造完earlySingletonObject实例暴露出去呢?

在笔者看来这个问题有2个方面的考虑:

  1. 从功能上讲,通过getEarlyBeanReference提前暴露bean实例后可能会由于开发规范性的问题导致最终它与初始化后获得的对象不一致(具体可阅读上文)。因此Spring只有在真正发生了循环依赖时才通过getEarlyBeanReference提前暴露bean实例,其他情况下则尽量避免。
  2. 从设计上讲,通过getEarlyBeanReference提前暴露bean实例会破坏了bean实例化原有的执行流程:实例化->属性填充->实例初始化(添加AOP代理),使得它变为:添加AOP代理->实例化->属性填充->实例初始化,这可能会产生除了上述第1种所产生的问题,还可能会导致其他异常的发生。因此Spring只有在真正发生了循环依赖时才通过getEarlyBeanReference提前暴露bean实例,其他情况下则尽量避免(常常在迭代中会用这种手法去兼容代码,而不是直接作暴力修改处理)。

这也解释了为什么Spring使用了三级缓存来解决循环依赖,而不是二级缓存。

最后,回到getSingleton(String, ObjectFactory)方法在执行完createBean方法生成bean实例后接着执行后置处理afterSingletonCreation将当前beanNamesingletonsCurrentlyInCreation中移除(避免再次创建导致异常的发生),然后执行addSingleton方法将最终完整版的bean添加到三级缓存singletonObjects中,同时在二级缓存earlySingletonObjects和一级缓存singletonFactories中将beanName对应的缓存实例移除。

java 复制代码
/**
 * Add the given singleton object to the singleton cache of this factory.
 * <p>To be called for eager registration of singletons.
 * @param beanName the name of the bean
 * @param singletonObject the singleton object
 */
protected void addSingleton(String beanName, Object singletonObject) {
    this.singletonObjects.put(beanName, singletonObject);
    this.singletonFactories.remove(beanName);
    this.earlySingletonObjects.remove(beanName);
}

至此,结束单例模式(Singleton)下bean的创建(含循环依赖)。

多例模式的bean创建

相比于单例模式,多例模式的bean创建就相对来说简单得多。首先,在多例模式下没有三级缓存的查询,而是直接通过isPrototypeCurrentlyInCreation方法判断prototypesCurrentlyInCreation属性是否包含当前beanName,若包含则表示循环依赖的发生,抛出BeanCurrentlyInCreationException异常;否则表示prototypesCurrentlyInCreation属性不包含当前beanName,开始执行多例模式的bean创建。下面,我们看到具体的代码步骤:

java 复制代码
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws ... {

    String beanName = ...;
    Object beanInstance;

    
    // Fail if we're already creating this bean instance:
    // We're assumably within a circular reference.
    if (isPrototypeCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
    
    // ...

    RootBeanDefinition mbd = ...; 
    
    // ...

    // Create bean instance.
    // It's a prototype -> create a new instance.
    Object prototypeInstance = null;
    try {
        beforePrototypeCreation(beanName);
        prototypeInstance = createBean(beanName, mbd, args);
    }
    finally {
        afterPrototypeCreation(beanName);
    }
    beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);

    return adaptBeanInstance(name, beanInstance, requiredType);
}

/**
 * Return whether the specified prototype bean is currently in creation
 * (within the current thread).
 * @param beanName the name of the bean
 */
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    return (curVal != null &&
            (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}

与单例模式类似,在多例模式下会在bean的创建前后加上前置处理beforePrototypeCreation方法和后置处理afterPrototypeCreation方法,具体代码如下所示:

java 复制代码
/** Names of beans that are currently in creation. */
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
        new NamedThreadLocal<>("Prototype beans currently in creation");

/**
 * Callback before prototype creation.
 * <p>The default implementation register the prototype as currently in creation.
 * @param beanName the name of the prototype about to be created
 * @see #isPrototypeCurrentlyInCreation
 */
protected void beforePrototypeCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    if (curVal == null) {
        this.prototypesCurrentlyInCreation.set(beanName);
    }
    else if (curVal instanceof String) {
        Set<String> beanNameSet = new HashSet<>(2);
        beanNameSet.add((String) curVal);
        beanNameSet.add(beanName);
        this.prototypesCurrentlyInCreation.set(beanNameSet);
    }
    else {
        Set<String> beanNameSet = (Set<String>) curVal;
        beanNameSet.add(beanName);
    }
}

/**
 * Callback after prototype creation.
 * <p>The default implementation marks the prototype as not in creation anymore.
 * @param beanName the name of the prototype that has been created
 * @see #isPrototypeCurrentlyInCreation
 */
protected void afterPrototypeCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    if (curVal instanceof String) {
        this.prototypesCurrentlyInCreation.remove();
    }
    else if (curVal instanceof Set) {
        Set<String> beanNameSet = (Set<String>) curVal;
        beanNameSet.remove(beanName);
        if (beanNameSet.isEmpty()) {
            this.prototypesCurrentlyInCreation.remove();
        }
    }
}

在前置处理beforePrototypeCreation会将当前请求的beanName加入到prototypesCurrentlyInCreation中;而在后置处理afterPrototypeCreation中则将当前请求的beanNameprototypesCurrentlyInCreation中移除。结合isPrototypeCurrentlyInCreation方法的判断,如果在请求多例bean(假设依赖也是多例bean)的过程中发生了循环依赖(在多例模式下特指具有相同beanNamebean)则必然会发生异常(由于没有提前暴露与缓存机制,不区分是构造器自动装配还是Setter方法自动装配)。

根据多例模式的特性,如果在遇到对同一beanName的循环依赖时如果不通过异常提前终止创建流程,则会造成bean在创建过程中不断地循环创建对象。

text 复制代码
                            +
                            |
                            |
                            v
           +----------------+-----------------+
  throw ex |                                  |
<----------+  isPrototypeCurrentlyInCreation  +<----------+
           |                                  |           |
           +----------------+-----------------+           |
                            |                             |
                            |                             |
                            v                             |
              +-------------+--------------+              |
              |                            |              |
              |  beforePrototypeCreation   |              |
              |                            |              |
              +-------------+--------------+              |
                            |                             |
                            |                             |
                            v                             |
                     +------+-------+                     |
                     |              |                     |
                     |  createBean  +---------------------+
                     |              | circular references
                     +------+-------+
                            |
                            |
                            v
               +------------+-------------+
               |                          |
               |  afterPrototypeCreation  |
               |                          |
               +------------+-------------+
                            |
                            |
                            v

回归到正常流程上,如果在前置处理beforePrototypeCreation没有发现循环依赖,那么Spring就会进入到createBean方法执行bean实例的创建。

java 复制代码
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {

    RootBeanDefinition mbdToUse = mbd;

    //...

    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    return beanInstance;
}

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

    // 1. 执行`raw bean`实例的创建
    BeanWrapper instanceWrapper = ...createBeanInstance(beanName, mbd, args);
    Object bean = instanceWrapper.getWrappedInstance();

    // ...       

    Object exposedObject = bean;
    // 2. 执行`raw bean`实例的属性填充
    populateBean(beanName, mbd, instanceWrapper);
    // 3. 执行`raw bean`实例的初始化
    exposedObject = initializeBean(beanName, exposedObject, mbd);
    
    // ...

    return exposedObject;
}

相比于单例模式,多例模式由于没有提前暴露和缓存的机制,createBean方法的执行就简单得多,具体执行流程如下所示:

  1. 执行raw bean实例的创建
  2. 执行raw bean实例的属性填充
  3. 执行raw bean实例的初始化

在执行流程上看仅是保留了基本的raw bean创建流程,那再结合上文前置方法beforePrototypeCreation和后置方法afterPrototypeCreation的处理,若在populateBean属性填充时发生了循环依赖就会触发isPrototypeCurrentlyInCreation的判断抛出BeanCurrentlyInCreationException异常,具体可参考以下流程图:

text 复制代码
                                   +
                                   |
                                   |
               +----------------------------------------+
               |                   |                    |
               |                   v                    |
               |  +----------------+-----------------+  |
     throw ex  |  |                                  |  |     circular references
<-----------------+  isPrototypeCurrentlyInCreation  +-----------------------------------+
               |  |                                  |  |                                |
               |  +----------------+-----------------+  |                                |
               |                   |                    |                                |
               |                   |                    |                                |
               |                   v                    |                                |
               |     +-------------+--------------+     |                                |
               |     |                            |     |                                |
               |     |  beforePrototypeCreation   |     |                                |
               |     |                            |     |                                |
               |     +-------------+--------------+     |                                |
               |                   |                    |                                |
               |                   |                    |                                |
               |                   v                    |                                |
               |            +------+-------+            |     +----------------------+   |
               |            |              |            |     |                      |   |
               |            |  createBean  +----------------->+  createBeanInstance  |   |
               |            |              |            |     |                      |   |
               |            +------+-------+            |     +----------+-----------+   |
               |                   |                    |                |               |
               |                   |                    |                |               |
               |                   v                    |                v               |
               |      +------------+-------------+      |        +-------+--------+      |
               |      |                          |      |        |                |      |
               |      |  afterPrototypeCreation  |      |        |  populateBean  +------+
               |      |                          |      |        |                |
               |      +------------+-------------+      |        +-------+--------+
               |                   |                    |                |
               +----------------------------------------+                | continue
                                   |                                     |
                                   v                                     v

需要注意,上述讨论的是请求bean与依赖bean都为多例模式的情况,而如果请求bean或者依赖bean任意一个不是多例模式(例如,单例模式)则需要结合对应作用域的bean创建流程进行分析,由于篇幅有限就不过多展开了。

至此,结束多例模式(Prototype)下bean的创建(含循环依赖)。

循环依赖的结论

总的来说,如果在Spring创建raw bean的阶段中发生了循环依赖是无法被解决而抛出异常的,因为在这个阶段中Spring还尚未将bean提前暴露出去,典型的有通过构造器完成自动装配发生了循环依赖。而如果Springraw bean的属性填充阶段发生了循环依赖,则会被Spring通过将bean提前暴露出去的方式顺利化解掉,典型的有使用字段的自动装配和Setter方法的自动装配来完成依赖注入。

需要注意的是,Spring在处理工厂方法参数的自动装配时会将它们当作是构造参数的自动装配来处理,也就是说通过工厂方法完成自动装配时发生了循环依赖也会抛出异常。

除此之外,Spring在进行bean的实例化过程中还是会存在一些比较隐匿比较特殊的情况会导致bean在发生循环依赖时抛出异常。例如,在@Configuration的配置类中存在自动装配的属性与通过实例工厂方法创建的bean有相互依赖的关系时可能会因循环依赖抛出异常,具体如下所示:

java 复制代码
@Configuration
public class Config {

    @Autowired
    private List<BaseService> serviceList;

    @Bean
    public CircularReferenceService circularReferenceService(){
        return new CircularReferenceService(serviceList);
    }
}

@Service
public class ServiceFirst implements BaseService {
    @Autowired
    private CircularReferenceService circularReferenceService;
}

@Service
public class ServiceSecond extends BaseService {
    @Autowired
    private CircularReferenceService circularReferenceService;
}

在上述场景中,如果ServiceFirst或者ServiceSecond先于Config被加载就会发生循环依赖异常。要清晰解释其中发生异常的原理需要我们再次回顾上文《源码分析:Bean的创建》章节中提到"关于通过实例工厂方法声明bean"的实例化流程,即在通过实例工厂方法创建bean实例前会提前获取当前实例工厂方法所在的工厂实例,具体代码如下所示:

java 复制代码
// factoryBeanName属性指的是工厂实例bean的名称,而不是FactoryBean实例的名称
String factoryBeanName = mbd.getFactoryBeanName();
if (factoryBeanName != null) {
    //...
    factoryBean = this.beanFactory.getBean(factoryBeanName);
}

// ...

在这里Spring就会使用BeanFactory#getBean来获取工厂实例。

在有了这一层认知之后,我们结合上文关于"Bean的创建"章节和"Bean的循环依赖"章节的分析推演出以下执行流程:

  1. 执行ServiceFirstbean实例创建。
  2. 将早期ServiceFirstbean实例提前暴露。
  3. 执行ServiceFirst#CircularReferenceService的属性填充,触发CircularReferenceService的创建。
  4. 执行CircularReferenceService的创建(将其加入到了singletonsCurrentlyInCreation属性中),由于是通过实例工厂方法声明的,所以在CircularReferenceService创建前触发了Config的创建(尚未将早期CircularReferenceServicebean实例提前暴露)。
  5. 执行Configbean实例创建。
  6. 执行Config#serviceList的属性填充,触发BaseService类(含ServiceFirstServiceSecond)的创建。
  7. 执行ServiceFirstbean实例创建,由于在缓存中查询到被早期暴露ServiceFirst实例(在第2步中被暴露),所以结束ServiceFirst的再次创建。
  8. 执行ServiceSecondbean实例创建。
  9. 执行ServiceSecond#CircularReferenceService的属性填充,触发CircularReferenceService的创建(在缓存中尚未添加CircularReferenceService实例)。
  10. 执行CircularReferenceService的创建,由于在第4步首次触发创建时已经将CircularReferenceService加入到了singletonsCurrentlyInCreation属性中,在此再次创建CircularReferenceService时判断singletonsCurrentlyInCreation属性中存在CircularReferenceService,因此抛出循环依赖异常。

在上述流程中,我们可以看到由于Spring先触发了ServiceFirst而导致了循环依赖异常的发生;类似地,如果我们先触发了ServiceSecond也同样会触发循环依赖异常的发生。那如果我们先触发了Config配置类的加载是不是就不会产生问题了呢?下面我们可以来模拟一下整个流程:

  1. 执行Configbean实例创建。
  2. 将早期Configbean实例提前暴露。
  3. 执行Config#serviceList的属性填充(属性加载阶段),触发BaseService类(含ServiceFirstServiceSecond)的创建。
  4. 执行ServiceFirstbean实例创建。
  5. 执行ServiceFirst#CircularReferenceService的属性填充,触发CircularReferenceService的创建。
  6. 由于CircularReferenceService是通过实例工厂方法声明的,所以在CircularReferenceService创建前触发了Config的创建。
  7. 执行Configbean实例创建,由于在缓存中查询到被早期暴露Config实例(在第2步中被暴露),所以结束Config的再次创建。
  8. 执行CircularReferenceService的创建,将Config#serviceList的属性传入CircularReferenceService构造器中进行创建,但由于Config#serviceList属性尚处于加载阶段而没有将值真正设置到属性中,所以此时传入nullCircularReferenceService构造器中进行构建(意味着CircularReferenceService中的serviceList属性为null)。
  9. 完成CircularReferenceService的创建(含属性填充完成和实例初始化完成),并将CircularReferenceService实例加入到缓存中。
  10. 完成ServiceFirstbean实例创建(含属性填充完成和实例初始化完成),并将ServiceFirst实例加入到缓存中。
  11. 执行ServiceSecondbean实例创建。
  12. 执行ServiceSecond#CircularReferenceService的属性填充,触发CircularReferenceService的创建。
  13. 执行CircularReferenceServicebean实例创建,由于在缓存中查询到已被实例化完成的CircularReferenceService实例(在第9步中将构建完成的CircularReferenceService实例加入到缓存),所以结束CircularReferenceService的再次创建。需要注意,在缓存中CircularReferenceServiceserviceList属性为null
  14. 完成ServiceSecondbean实例创建(含属性填充完成和实例初始化完成),并将ServiceSecond实例加入到缓存中。
  15. 执行Config#serviceList的属性填充(属性赋值阶段),在赋值完成后Config#serviceList的属性值为包含ServiceFirst实例和ServiceSecond实例的列表。
  16. 完成Configbean实例创建(含属性填充完成和实例初始化完成),并将Config实例加入到缓存中。

在上述模拟流程中,我们可以看到先触发了Config配置类的加载同样会产生其他问题,即在属性填充前期Spring尚未将值设置到Config#serviceList中时触发了CircularReferenceService的加载导致传入其构造器的Config#serviceList属性为null,进而使得构建完成的CircularReferenceService实例中serviceList属性为null(赋值失败)。显然地,这是比循环依赖更为致命的异常,因为它会在运行时让程序发生异常而不是在启动加载时。

综上所述,一般情况下如果在Spring创建raw bean的阶段中发生了循环依赖是无法被解决而抛出异常的,而如果Springraw bean的属性填充阶段发生了循环依赖则会被Spring通过将bean提前暴露出去的方式顺利化解掉。但这并不是绝对的法则,在特殊情况下属性填充阶段发生的循环依赖也是有可能会抛出异常的(甚至会产生更为致命的异常),这取决于我们在开发中是如何使用Spring IoC的。

至此,我们完成了整个Bean循环依赖的分析章节。

相关推荐
小金的学习笔记6 分钟前
隐藏指定文件/文件夹和自动提示功能消失解决方案
java·idea
Q_19284999067 分钟前
基于Spring Boot的个性化推荐外卖点餐系统
java·spring boot·后端
会说法语的猪20 分钟前
IDEA使用Alt + Enter快捷键自动接受返回值一直有final修饰的问题处理
java·ide·intellij-idea
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ29 分钟前
MyBatis执行完sql后,返回的数值代表的意思
java·开发语言
CodeClimb1 小时前
【华为OD-E卷-寻找密码 100分(python、java、c++、js、c)】
java·python·华为od
爱上语文1 小时前
宠物管理系统:Service层
java·开发语言·宠物
解梦者1 小时前
Spring(七)Spring Cloud----Feign、Zuul和Apollo
spring·spring cloud·feign·apollo·zuul
水w1 小时前
【项目实践】SpringBoot Nacos配置管理 map数据
java·服务器·开发语言·spring boot·nacos
@菜鸟进阶记@1 小时前
SpringBoot核心:自动配置
java·spring boot·后端
瓜牛_gn1 小时前
苍穹外卖项目Day02代码结构深度解析
java·spring