SpringloC容器的依赖注入源码解析(8)—— 单例循环依赖的解决

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 这一讨论的前提是要对Spring的doCreateBean方法有所了解,故将其源码放在这里,以供参考:

这一讨论的前提是要对Spring的doCreateBean方法有所了解,故将其源码放在这里,以供参考:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
   // Instantiate the bean.
   // bean实例包装类
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      // 从未完成创建的包装Bean缓存中清理并获取相关中的包装Bean实例,毕竟是单例的,只能存一份
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      //创建bean的时候,这里创建bean的实例有三种方法
      // 1.工厂方法创建
      // 2.构造方法的方式注入
      // 3.无参构造方法注入
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   final Object bean = instanceWrapper.getWrappedInstance();
   Class<?> beanType = instanceWrapper.getWrappedClass();
   if (beanType != NullBean.class) {
      mbd.resolvedTargetType = beanType;
   }
   // Allow post-processors to modify the merged bean definition.
   // 调用BeanDefinition属性合并完成后的BeanPostProcessor后置处理器
   synchronized (mbd.postProcessingLock) {
      if (!mbd.postProcessed) {
         try {
            // 被@Autowired、@Value标记的属性在这里获取
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
         }
         catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                  "Post-processing of merged bean definition failed", ex);
         }
         mbd.postProcessed = true;
      }
   }
   // 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));
   }
   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
      populateBean(beanName, mbd, instanceWrapper);
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   catch (Throwable ex) {
      if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
         throw (BeanCreationException) ex;
      }
      else {
         throw new BeanCreationException(
               mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
      }
   }
   if (earlySingletonExposure) {
      Object earlySingletonReference = getSingleton(beanName, false);
      if (earlySingletonReference != null) {
         if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
         }
         else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
               if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                  actualDependentBeans.add(dependentBean);
               }
            }
            if (!actualDependentBeans.isEmpty()) {
               throw new BeanCurrentlyInCreationException(beanName,
                     "Bean with name '" + beanName + "' has been injected into other beans [" +
                     StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                     "] in its raw version as part of a circular reference, but has eventually been " +
                     "wrapped. This means that said other beans do not use the final version of the " +
                     "bean. This is often the result of over-eager type matching - consider using " +
                     "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
            }
         }
      }
   }
   // Register bean as disposable.
   try {
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }
   return exposedObject;
}


对应的文章《SpringloC容器的依赖注入源码解析(7)—— doCreateBean之剩余逻辑(解决循环依赖的源头)》


现在有A依赖B,B也依赖A,假设A先创建,就会来到doCreateBean方法里,会经过各种包装,在被

instanceWrapper = createBeanInstance(beanName, mbd, args);


处理了之后生成一个没有任何属性的A的实例。


之后在调用


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


14.png

之前将A实例放入到ObjectFactory里面,然后调用addSingletonFactory将ObjectFactory添加到三级缓存里,此时只有三级缓存保存了A对应的ObjectFactory实例。


随后就会调用


populateBean(beanName, mbd, instanceWrapper);


给属性赋值,由于A依赖于B,在populate里会尝试获取实例B,由于B还没有被创建过,所以又递归的调用doCreateBean方法。


在doCreateBean方法里调用


instanceWrapper = createBeanInstance(beanName, mbd, args);


创建出实例B,将其对应的实例工厂放入到三级缓存中,此时三级缓存中就保存了A和B的实例,随后在B里又会执行

populateBean(beanName, mbd, instanceWrapper);


由于B依赖A,所以在populate里尝试获取A的实例,此时调用的是AbstractBeanFactory的doGetBean方法,在里面调用:

Object sharedInstance = getSingleton(beanName);

从三级缓存里获取A实例对应的ObjectFactory实例


15.png


这里getObject()调用的就是getEarlyBeanReference方法


16.png


获取到A实例之后就会将A实例放入二级缓存里,同时清空三级缓存里的A


此时就回到创建B的doCreateBean的

populateBean(beanName, mbd, instanceWrapper);


A被注入到B里面了,即当B执行完populateBean之后就已经获取到了A,此时B就会执行完剩余逻辑获得到一个完备的B,此时方法返回到doGetBean方法里


17.png


在getSingleton方法里面会最终调用

addSingleton(beanName, singletonObject);


在方法里会将实例B添加到一级缓存里,并将B从二三级缓存里移除,表示已经彻底完成了B实例的创建,之后返回B。


B之所以会被创建是因为A调用了


populateBean(beanName, mbd, instanceWrapper);

此时又回到这个地方,A内部已经赋值上了完备的B,之后步骤同上,调用addSingleton将A放入一级缓存里,此时循环依赖的支持完成。


整个的流程图如下图所示,大家可以基于前面的介绍看一下到底有没有掌握 ~:


18.png




相关文章
|
1月前
|
Kubernetes Cloud Native 持续交付
Docker:轻量级容器化技术解析
Docker:轻量级容器化技术解析
|
3月前
|
存储 缓存 安全
Java 集合容器常见面试题及详细解析
本文全面解析Java集合框架,涵盖基础概念、常见接口与类的特点及区别、底层数据结构、线程安全等内容。通过实例讲解List(如ArrayList、LinkedList)、Set(如HashSet、TreeSet)、Map(如HashMap、TreeMap)等核心组件,帮助读者深入理解集合容器的使用场景与性能优化。适合准备面试或提升开发技能的开发者阅读。
66 0
|
5月前
|
弹性计算 Java Maven
从代码到容器:Cloud Native Buildpacks技术解析
Cloud Native Buildpacks(CNB)是一种标准化、云原生的容器镜像构建系统,旨在消除手动编写Dockerfile,提供可重复、安全且高效的构建流程。它通过分层策略生成符合OCI标准的镜像,实现应用与基础镜像解耦,并自动化依赖管理和更新。阿里云应用管理支持通过CNB技术一键部署应用至ECS,简化构建和运行流程。
|
6月前
|
消息中间件 JavaScript 前端开发
最细最有条理解析:事件循环(消息循环)是什么?为什么JS需要异步
度一教育的袁进老师谈到他的理解:单线程是异步产生的原因,事件循环是异步的实现方式。 本质是因为渲染进程因为计算机图形学的限制,只能是单线程。所以需要“异步”这个技术思想来解决页面阻塞的问题,而“事件循环”是实现“异步”这个技术思想的最主要的技术手段。 但事件循环并不是全部的技术手段,比如Promise,虽然受事件循环管理,但是如果没有事件循环,单一Promise依然能实现异步不是吗? 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您
|
8月前
|
Kubernetes Linux 虚拟化
入门级容器技术解析:Docker和K8s的区别与关系
本文介绍了容器技术的发展历程及其重要组成部分Docker和Kubernetes。从传统物理机到虚拟机,再到容器化,每一步都旨在更高效地利用服务器资源并简化应用部署。容器技术通过隔离环境、减少依赖冲突和提高可移植性,解决了传统部署方式中的诸多问题。Docker作为容器化平台,专注于创建和管理容器;而Kubernetes则是一个强大的容器编排系统,用于自动化部署、扩展和管理容器化应用。两者相辅相成,共同推动了现代云原生应用的快速发展。
2327 11
|
9月前
|
开发框架 安全 开发者
Docker 是一种容器化技术,支持开发者将应用及其依赖打包成容器,在不同平台运行而无需修改。
Docker 是一种容器化技术,支持开发者将应用及其依赖打包成容器,在不同平台运行而无需修改。本文探讨了 Docker 在多平台应用构建与部署中的作用,包括环境一致性、依赖管理、快速构建等优势,以及部署流程和注意事项,展示了 Docker 如何简化开发与部署过程,提高效率和可移植性。
236 4
|
9月前
|
负载均衡 网络协议 算法
Docker容器环境中服务发现与负载均衡的技术与方法,涵盖环境变量、DNS、集中式服务发现系统等方式
本文探讨了Docker容器环境中服务发现与负载均衡的技术与方法,涵盖环境变量、DNS、集中式服务发现系统等方式,以及软件负载均衡器、云服务负载均衡、容器编排工具等实现手段,强调两者结合的重要性及面临挑战的应对措施。
317 3
|
10月前
|
安全 持续交付 Docker
深入理解并实践容器化技术——Docker 深度解析
深入理解并实践容器化技术——Docker 深度解析
415 2
|
10月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
10月前
|
运维 持续交付 虚拟化
深入解析Docker容器化技术的核心原理
深入解析Docker容器化技术的核心原理
228 1

推荐镜像

更多
  • DNS