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

具体步骤如下:

  1. CircleClassA 在实例化的时候 首先从缓存中获取不到,然后进入创建方法,接着将CircleClassA加入到singletonsCurrentlyInCreation中,并在singletonFactories加入一个getEarlyBeanReference,表示当前CircleClassA正在创建中。
  2. 当CircleClassA填充属性的值populateBean时,发现依赖了CircleClassB,触发CircleClassB的实例化。
  3. 实例化CircleClassB,首先从缓存中获取不到,然后进入创建方法,接着将CircleClassB加入到singletonsCurrentlyInCreation中,并在singletonFactories加入一个getEarlyBeanReference,表示当前CircleClassB正在创建中。
  4. 当CircleClassB填充属性的值populateBean时,发现依赖了CircleClassA,触发CircleClassA的实例化。
  5. 再次进入CircleClassA 的实例化方法,此时虽然singletonObjects中获取不到CircleClassA,但是检测到CircleClassA存在早期暴露的实例因此尝试从earlySingletonObjects中获取,首次调用获取不到从singletonFactories中获取,取到之后将CircleClassA放入earlySingletonObjects,并提供给CircleClassB填充属性的值populateBean时使用。(此时的CircleClassA只是个引用的地址,实际上并不是一个完整的CircleClassA)。
  6. 此时CircleClassB已经完成了(内部依赖的CircleClassA是个不完整的实例)并提供给CircleClassA填充属性的值populateBean时使用。CircleClassA完成了CircleClassB的注入,它变成了一个完整的实例。
  7. 又由于CircleClassB中引用了CircleClassA的一个地址。所以它也同时变成了一个完整的。
  8. 实例化完成之后删除早期引用map,并放入单例map中缓存singletonObjects。


程序员的核心竞争力其实还是技术,因此对技术还是要不断的学习,关注 “IT巅峰技术” 公众号 ,该公众号内容定位:中高级开发、架构师、中层管理人员等中高端岗位服务的,除了技术交流外还有很多架构思想和实战案例。

作者是 《 消息中间件 RocketMQ 技术内幕》 一书作者,同时也是 “RocketMQ上海社区”联合创始人,曾就职于拼多多、德邦等公司,现任上市快递公司架构负责人,主要负责开发框架的搭建、中间件相关技术的二次开发和运维管理、混合云及基础服务平台的建设。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值