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

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: doCreateBean之创建无属性的beandoCreateBean之处理@Autowired以及@Value标签

前置文章:


doCreateBean之创建无属性的bean

doCreateBean之处理@Autowired以及@Value标签


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


接上文分析到的位置,接下来执行:


// 向容器中缓存单例模式的Bean对象,以防循环引用
// 判断是否是早期引用的bean,如果是,则允许其提前暴露引用
// 这里判断的逻辑主要有三个:
// 1.是否为单例
// 2.是否允许循环引用
// 3.是否是在创建中的bean
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));


之后会来到

// 这里是一个匿名内部类,为了防止循环引用,尽早持有对象的引用
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));


进入到addSingletonFactory:

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);
         // 消除此Bean在二级缓存里的缓存信息
         this.earlySingletonObjects.remove(beanName);
         // 这里为了记录注册单例的顺序
         this.registeredSingletons.add(beanName);
      }
   }
}


消除此Bean在二级缓存里的缓存信息,将其包装成singletonFactory实例往三级缓存里添加


这里有一个重点就是Spring解决循环依赖的真相就在这一段源码中:在这里beanFactory被put进了singletonFactories,此时的bean只是完成了初始化构造的bean,还没有进行set或者注解注入的bean,是bean的一个中间状态,但是已经能被人认出来了,所以Spring此时将这个对象提前曝光出来让大家认识、使用。


回到doCreateBean,进入到getEarlyBeanReference


protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
   Object exposedObject = bean;
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         // 如果是SmartInstantiationAwareBeanPostProcessor类型,就进行处理,
         // 如果没有相关处理内容,就返回默认的实例。
         // 里面的AbstractAutoProxyCreator类是后续AOP的关键
         if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
            SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
            // 对单例进行AOP包装并返回的地方!
            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
         }
      }
   }
   return exposedObject;
}


注意这个方法并不是在doCreateBean的

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

这一行执行的,这里只是将getEarlyBeanReference注册进去,实际执行的地方是前面尝试从缓存里获取bean的地方,即AbstractBeanFactory的doCreateBean方法里。

回到doCreateBean,此时获取到了bean,并且只在三级缓存里面保存。

之后会执行


populateBean(beanName, mbd, instanceWrapper);

来将bean的属性真正注入到里面,然后再调用initializeBean进行彻底的初始化

// Initialize the bean instance.
// Bean对象的初始化,依赖注入在此触发
// 这个exposedObject在初始化完成之后返回作为依赖注入完成后的Bean
Object exposedObject = bean;
try {
   // 填充bean实例的属性
   populateBean(beanName, mbd, instanceWrapper);
   // 初始化bean,过程如下:
   // 1. 判断是否实现了BeanNameAware, BeanClassLoaderAware, BeanFactoryAware方法,如果有,则设置相关的属性
   // 2. 调用bean初始化的前置(BeanPostProcessor)操作
   // 3. 执行初始化的方法
   //       如果有initializingBean,则调用afterPropertiesSet
   //       如果有InitMethod,则调用初始方法
   // 4. 调用bean初始化的后置(BeanPostProcessor)操作
   exposedObject = initializeBean(beanName, exposedObject, mbd);
}



之后判断该bean是否允许提前暴露,

if (earlySingletonExposure) {
   // 获取指定名称的已注册的单例模式Bean对象
   Object earlySingletonReference = getSingleton(beanName, false);
   if (earlySingletonReference != null) {
      // 如果经过initializeBean执行后返回的bean还是同一个(不是代理对象实例,即没有被增强)
      if (exposedObject == bean) {
         // 确保根据名称获取到的的已注册的Bean和正在实例化的Bean是同一个
         exposedObject = earlySingletonReference;
      }
     ......
   }
}


if 里面调用了getSingleton方法,进入该方法:

@Nullable
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已经被放到三级缓存里了,所以会被从三级缓存里取出


singletonObject = singletonFactory.getObject();

获取到bean实例之后会将其从三级缓存里移除


this.singletonFactories.remove(beanName);

回到doCreateBean,

Object earlySingletonReference = getSingleton(beanName, false);


执行完毕之后再判断一下

if (exposedObject == bean) {
   // 确保根据名称获取到的的已注册的Bean和正在实例化的Bean是同一个
   exposedObject = earlySingletonReference;
}


exposedObject由于之前调用了initializeBean方法,所以有可能会被赋值为全新的对象,一旦被赋值了全新的对象之后if里面是false,进入到前面的initializeBean方法里

12.png


重点关注applyBeanPostProcessorsAfterInitialization方法,进入到方法里:


13.png

重点关注实现了postProcessAfterInitialization方法的AbstractAutoProxyCreator类


@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}


该方法尝试从缓存里获取bean实例并返回,即如果先前加工过的话就返回bean实例,确保加工过的bean实例和先前是同一个。


回到doCreateBean,


if (exposedObject == bean) {
   // 确保根据名称获取到的的已注册的Bean和正在实例化的Bean是同一个
   exposedObject = earlySingletonReference;
}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
   String[] dependentBeans = getDependentBeans(beanName);
   Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
   // 获取依赖于当前Bean的Bean实例
   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.");
   }
}

如果不是同一个bean,则有可能经过initializeBean之后没有返回同样的bean实例,之所以会出现这个问题是因为postProcessor也是供用户使用的,用户可能在自定义逻辑里修改了bean实例,用户的行为对于系统来说是无法预测的。



不一样的话就会检查有没有别的bean依赖于这个bean,如果存在则直接报错,因为既然执行到这里其他bean实例就已经执行过了populate方法将该bean的实例设置上了,然而设置上了之后却又创建出了该bean的新实例,于是就不满足单例。



通过验证之后会注册销毁时候的回调逻辑


registerDisposableBeanIfNecessary(beanName, bean, mbd);



相关文章
|
10天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
10天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
10天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
29天前
|
PyTorch Shell API
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。
|
10天前
|
安全 搜索推荐 数据挖掘
陪玩系统源码开发流程解析,成品陪玩系统源码的优点
我们自主开发的多客陪玩系统源码,整合了市面上主流陪玩APP功能,支持二次开发。该系统适用于线上游戏陪玩、语音视频聊天、心理咨询等场景,提供用户注册管理、陪玩者资料库、预约匹配、实时通讯、支付结算、安全隐私保护、客户服务及数据分析等功能,打造综合性社交平台。随着互联网技术发展,陪玩系统正成为游戏爱好者的新宠,改变游戏体验并带来新的商业模式。
|
24天前
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
183 77
|
1月前
|
监控 Docker 容器
在Docker容器中运行打包好的应用程序
在Docker容器中运行打包好的应用程序
|
5天前
|
Ubuntu Linux 开发工具
docker 是什么?docker初认识之如何部署docker-优雅草后续将会把产品发布部署至docker容器中-因此会出相关系列文章-优雅草央千澈
Docker 是一个开源的容器化平台,允许开发者将应用程序及其依赖项打包成标准化单元(容器),确保在任何支持 Docker 的操作系统上一致运行。容器共享主机内核,提供轻量级、高效的执行环境。本文介绍如何在 Ubuntu 上安装 Docker,并通过简单步骤验证安装成功。后续文章将探讨使用 Docker 部署开源项目。优雅草央千澈 源、安装 Docker 包、验证安装 - 适用场景:开发、测试、生产环境 通过以上步骤,您可以在 Ubuntu 系统上成功安装并运行 Docker,为后续的应用部署打下基础。
docker 是什么?docker初认识之如何部署docker-优雅草后续将会把产品发布部署至docker容器中-因此会出相关系列文章-优雅草央千澈
|
11天前
|
存储 Kubernetes 开发者
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档
Docker 是一种开源的应用容器引擎,允许开发者将应用程序及其依赖打包成可移植的镜像,并在任何支持 Docker 的平台上运行。其核心概念包括镜像、容器和仓库。镜像是只读的文件系统,容器是镜像的运行实例,仓库用于存储和分发镜像。Kubernetes(k8s)则是容器集群管理系统,提供自动化部署、扩展和维护等功能,支持服务发现、负载均衡、自动伸缩等特性。两者结合使用,可以实现高效的容器化应用管理和运维。Docker 主要用于单主机上的容器管理,而 Kubernetes 则专注于跨多主机的容器编排与调度。尽管 k8s 逐渐减少了对 Docker 作为容器运行时的支持,但 Doc
73 5
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档
|
17天前
|
关系型数据库 应用服务中间件 PHP
实战~如何组织一个多容器项目docker-compose
本文介绍了如何使用Docker搭建Nginx、PHP和MySQL的环境。首先启动Nginx容器并查看IP地址,接着启动Alpine容器并安装curl测试连通性。通过`--link`方式或`docker-compose`配置文件实现服务间的通信。最后展示了Nginx配置文件和PHP代码示例,验证了各服务的正常运行。
43 3
实战~如何组织一个多容器项目docker-compose

推荐镜像

更多