Spring如何解决循环依赖

一、背景

我们都知道Spring可以通过xml,或者解析我们的注解,通过扫描所有资源文件,从而将所有匹配到的资源封装成为一个BeanDefinition注册到我们的BeanFactory中。

此时,Spring已经知道了所有我们想要注册到容器中的BeanDefinition,下一步就是将BeanDefinition实例化,这样才能提供出来给我们使用。

二、Spring中Bean的实例化

我们发现Spring整个加载过程都在AbstractApplicationContext.refresh()中去完成。

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
    // Prepare this context for refreshing. 准备刷新
    prepareRefresh();
    
// Tell the subclass to refresh the internal bean factory.
/*
 * 刷新内部BeanFactory
 * ClassPathXmlApplicationContext:1.新建BeanFactory,2.解析xml,3.封装成BeanDefintion对象
 * AnnotationConfigApplicationContext: 获取GenericApplicationContext中的beanFactory
 */
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
// 为BeanFactory进行必要的准备工作
prepareBeanFactory(beanFactory);

try {
    // Allows post-processing of the bean factory in context subclasses.
    // 进行额外的后置处理
    postProcessBeanFactory(beanFactory);

    // Invoke factory processors registered as beans in the context.
    // 执行1.BeanDefinitionResgistryPostProcessor、2.BeanFactoryPostProcessor的回调
    invokeBeanFactoryPostProcessors(beanFactory);

    // Register bean processors that intercept bean creation.
    // 实例化所有实现了BeanPostProcessor接口的类并注册到容器中去
    registerBeanPostProcessors(beanFactory);

    // Initialize message source for this context. 国际化
    initMessageSource();

    // Initialize event multicaster for this context. 初始化事件类
    initApplicationEventMulticaster();

    // Initialize other special beans in specific context subclasses. 子容器自定义实现
    onRefresh();

    // Check for listener beans and register them. 注册事件
    registerListeners();

    // Instantiate all remaining (non-lazy-init) singletons.
    //1.bean实例化,2.ioc 3.注解支持 4.BeanPostProssor执行 5.AOP入口
    finishBeanFactoryInitialization(beanFactory);

    // Last step: publish corresponding event.
    finishRefresh();
  } catch (BeansException ex) {
    if (logger.isWarnEnabled()) {
        logger.warn("Exception encountered during context initialization - " +
                "cancelling refresh attempt: " + ex);
    }

    // Destroy already created singletons to avoid dangling resources.
                destroyBeans();
    // Reset 'active' flag.
                cancelRefresh(ex);
    // Propagate exception to caller.
    throw ex;
            } finally {
    // Reset common introspection caches in Spring's core, since we
    // might not ever need metadata for singleton beans anymore...
    resetCommonCaches();
}

}

我们着重关注一下finishBeanFactoryInitialization方法,它是Spring实例化的入口方法。


  • 获取BeanFactory中所有的beanDefinition名称
  • 合并RootBeanDefinition
  • 非抽象的,单例的,非懒加载的就实例化
  • 是否实现了FactoryBean接口,如果是加一个&前缀调用内部的getObject,否则直接获取
  • 首先尝试从缓存中获取getSingleton(beanName),(首次获取必然获取不到)接着进入创建方法
  • 单例创建之前的操作:加入到正在创建的一个set集合中singletonsCurrentlyInCreation
  • 调到外部的匿名类中的实例化方法,如果有值已经创建成功singletonFactory.getObject();
  • 调到doCreateBean创建实例BeanWrapper
  • 允许早期引用加入单例工厂直接返回这个bean的引用。addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  • 填充属性的值populateBean
  • initializeBean


三、Spring容器如何解决循环依赖

什么是循环依赖


循环依赖就是循环引用,就是两个或多个 bean 相互之间的持有对方,比如 CircleA 引用 C ircleB , CircleB 引用 CircleC, CircleC 引用 CircleA,则它们最终反映为一个环。

@Component
public class CircleClassA {
public CircleClassA() {
        System.out.println("====CircleClassA====");
    }
}

@Component public class CircleClassB { public CircleClassB() { System.out.println("====CircleClassB===="); } }

首先我们需要明确的一点是:Spring只会处理上述类型的循环依赖(单例,非构造函数注入)其它情况直接报错。

Spring在处理Bean实例化的过程中是如何解决循环依赖的呢?我们需要着重关注如下3个Map。

singletonObjects

earlySingletonObjects

singletonFactories

具体步骤如下:

CircleClassA 在实例化的时候 首先从缓存中获取不到,然后进入创建方法,接着将CircleClassA加入到singletonsCurrentlyInCreation中,并在singletonFactories加入一个getEarlyBeanReference,表示当前CircleClassA正在创建中。

当CircleClassA填充属性的值populateBean时,发现依赖了CircleClassB,触发CircleClassB的实例化。

实例化CircleClassB,首先从缓存中获取不到,然后进入创建方法,接着将CircleClassB加入到singletonsCurrentlyInCreation中,并在singletonFactories加入一个getEarlyBeanReference,表示当前CircleClassB正在创建中。

当CircleClassB填充属性的值populateBean时,发现依赖了CircleClassA,触发CircleClassA的实例化。

再次进入CircleClassA 的实例化方法,此时虽然singletonObjects中获取不到CircleClassA,但是检测到CircleClassA存在早期暴露的实例因此尝试从earlySingletonObjects中获取,首次调用获取不到从singletonFactories中获取,取到之后将CircleClassA放入earlySingletonObjects,并提供给CircleClassB填充属性的值populateBean时使用。(此时的CircleClassA只是个引用的地址,实际上并不是一个完整的CircleClassA)。

此时CircleClassB已经完成了(内部依赖的CircleClassA是个不完整的实例)并提供给CircleClassA填充属性的值populateBean时使用。CircleClassA完成了CircleClassB的注入,它变成了一个完整的实例。

又由于CircleClassB中引用了CircleClassA的一个地址。所以它也同时变成了一个完整的。

实例化完成之后删除早期引用map,并放入单例map中缓存singletonObjects。




程序员的核心竞争力其实还是技术,因此对技术还是要不断的学习,关注“IT巅峰技术” ,该公众号内容定位:中高级开发、架构师、中层管理人员等中高端岗位服务的,除了技术交流外还有很多架构思想和实战案例,作者是“《 消息中间件 RocketMQ技术内幕》 ”一书作者,同时也是“RocketMQ上海社区”联合创始人,曾就职于拼多多、德邦等公司,现任上市快递公司架构负责人,主要负责开发框架的搭建、中间件相关技术的二次开发和运维管理、混合云及基础服务平台的建设;

请关注公众号,一起探讨、学习.


版权信息:本文由中间件兴趣圈创作

禁止非授权转载,违着依法追究相关法律责任

如需转载,请联系 codingw@126.com