getBean流程介绍(以单例的Bean流程为准)

getBean(beanName)

从BeanFactory中获取Bean的实例对象,真正获取的逻辑由doGetBean实现。

doGetBean(beanName, requiredType, args, typeCheckOnly)

获取Bean实例的逻辑。

1 transformedBeanName(beanName)——处理Bean的名字

transformedBeanName()对BeanName做一层转换,这层转换主要设计两方面的处理,一是对BeanFactory的Name做一些处理(通常FactoryBean前面会增加一个&符号用来和BeanName区分,例如假设一个FactoryBean的name为&myFactoryBean,那么我们它创建的bean的name就为myFactoryBean),另一个是针对别名的处理。

2 getSingleton(beanName, allowEarlyReferrence)——从三级缓存中搜索

getSingletion()方法会先检查BeanFactory.singletionObjects是否已经存在这个Bean的单例。

singletionObjectsBeanFactory内部的一个Map,存放已经是初始化完成的bean对象,其中key存放的是beanName,而value存放的是Bean实例。

如果singletionObjects中不存在但是该Bean是否正在创建中,则会尝试从earlySingletionObjects中获取。earlySingletionObjects也是一个map,存放的是已经创建出对象,但是还未设置属性的bean。

如果在earlySingletonObjects中依旧找不到Bean对象,那么要继续考虑通过ObjectFactory创建Bean对象的情况。

这里就引出了Spring中关于解决循环依赖问题的三级缓存。关于循环依赖的处理我们最后再分析。

  1. /** Cache of singleton objects: bean name --> bean instance */
  2. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  3. /** Cache of singleton factories: bean name --> ObjectFactory */
  4. private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  5. /** Cache of early singleton objects: bean name --> bean instance */
  6. private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

3 getParentBeanFactory().getBean()——从父容器中获取Bean

我们知道HierarchicalBeanFactory提供了BeanFactory父子容器的关系。子容器可以获取父容器中的Bean。

当我们从子容器中的三级缓存中获取不到Bean,且找不到该Bean的定义时,Spring会转而去父容器中查找,如果父容器中依旧没能找到,则说明该Bean不存在。如果父容器中存在,则从父容器中获得Bean。

4 markBeanAsCreated(beanName)

这步只是对这个BeanName做个标记,表示这个BeanName对应的Bean已经被创建或是正在被创建。

5 getMergedLocalBeanDefinition(beanName)

BeanDefinition是Spring中对Bean的定义和描述,其中难免会有很多属性和配置。在早起的xml时代,这些配置都需要人员编码,因此Spring对BeanDefition做了一种继承的机制,可以定义一个父定义,然后子类通过继承获得父定义中的属性(当然也支持覆盖父定义中的属性)。有点类似先定义一个宏。

而这个方法就是将父子BeanDefition整合一下,得到一个MergedBeanDefition。

6 getDependsOn()——处理DependsOn

Bean可以通过DependsOn来显示控制Bean的初始化顺序(和依赖注入时的隐式初始化顺序略有不同,显示的DependsOn不用要求两个Bean之间存在引用关系)。

Spring会优先创建DependsOn指定的Bean,这一过程中,也可能存在"循环依赖"的问题,但是这种情况下的"循环依赖"问题Spring是无法内部自己解决的(因为通常使用DependsOn,就是想强制让否个Bean优先启动)。

7 getSingleton(beanName, ObjectFactory)——获取Bean实例

这是对getSingleton方法的重载,前文版本中的getSingleton方法只会从三级缓存中搜索Bean的实例,而这个重载方法会真正去创建Bean的实例。

在真正实例化对象前,依旧会先从singletonObjects检查一次对象是否已经被创建,如果没有,方法就上锁,并进入实例化对象的流程。

7.1 beforeSingletonCreation(beanName)——创建前的处理

这步通常是做一些校验,并把该Bean标记成正在创建。

7.2 singletonFactory.getObject()——通过ObjectFactory创建对象

具体的对象创建过程会调用AbstractAutoWireCapableBeanFactory.createBean(beanName, beanDefinition, args)方法。

7.2.1 resolvedClass()——解析bean类型

这步主要针对bean类型做解析。

7.2.2 preparedMethodOverrides()——overrideMethod的处理

这里的overrideMthod主要是针对bean中的look-upreplace等配置。有兴趣的读者可以自己展开了解。

7.2.3 resolveBeforeInstantiation()——在真正创建bean前,给用户一次控制bean创建的过程

用户可以通过InstantiationAwarePostProcessor.postProcessBeforeInstantiation(beanClass, beanName)方法先行创建出Bean对象。

InstantiationAwarePostProcessor接口继承自BeanPostProcessor,主要是针对Bean的实例化前后阶段设置了回调。

如果在这步中返回了Bean的实例,那么Spring不会再执行默认的创建策略。

这步留给用户的拓展还是比较有用的,用户可以根据该特性为某个Bean设置代理。

7.2.4 doCreateBean(beanName, mbd, args)——创建bean的过程

Bean的创建过程可以分成两个步骤:

1).实例化Bean的对象

2).针对Bean实例做初始化

7.2.4.1 createBeanInstance()——创建bean实例

这个方法主要是对Bean实例的创建,创建方式顺序是BeanDefition的Supplier>BeanDefinition的FactoryMethod>构造器反射创建。

7.2.4.2 applyMergedBeanDefinitionPostProcessors()

应用MergedBeanDefinitionPostProcessor,对MergedBeanDefinition做后处理。

7.2.4.3 addSingletonFactory(beanName, ObjectFactory)

将创建的Bean封装到ObjectFactory中,然后添加到singletonFactories中(三级缓存)。

这样在出现循环依赖的时候,可以从三级缓存中获取到这个Bean的引用,虽然此时这个Bean还未完全准备好。

7.2.4.4 poplulateBean(beanName, mdb, instanceWrapper)

填充Bean的属性。被创建出来的Bean对象会在这一步被设置属性。

Spring会根据Bean的AutoWireType进行属性的装配。

注意装配的过程中可能发生循环依赖。

7.2.4.5 initializeBean(beanName, exposedObject, mdb)——调用初始化方法

经过7.2.4.4 其实Bean对象已经创建的差不多了,之所以还需要在进行初始化是因为一些Bean可能指定了init方法,做一些特殊的初始化动作。这一步就是为了能够执行这些逻辑。

7.2.4.6 registerDisposableBeanIfNecessary()

为Bean注册销毁时的回调。

7.3 afterSingletonCreation(beanName)——创建后的处理

同样是做一些校验,并且将bean从正在创建的状态中移除。

7.4 addSingleton()——添加单例

这一步Spring会将创建好的Bean添加到singletonObjects中(一级缓存,用来存放已经创建好的bean),并从earlySingletonObjects和singletonFactories(二级缓存和三级缓存)中移除这个bean的引用。

8 校验类型

在getSingleton()执行完成之后,已经得到了bean的对象,这一步主要是针对Bean类型校验,看是否得到的实例是否是期望的类型,如果不是就对类型进行转换。

转换失败,则抛出异常。

9 返回bean

将得到的Bean返回给调用者。完成获取Bean的过程。

通过流程图加深印象:


循环依赖

什么是循环依赖

循环依赖是Spring容器在初始化Bean时,发现两个Bean互相依赖的一种特殊情况。

假设这么一种情况:

ComponentAComponentB为同一容器中的两个bean,且他们装配时都需要装配对方的引用:

  1. @Component
  2. public class ComponentA {
  3. @Autowired
  4. ComponentB b;
  5. }
  1. @Component
  2. public class ComponentB {
  3. @Autowired
  4. ComponentA a;
  5. }

那么在创建ComponentA时,发现需要装配ComponentB,转而去创建ComponentB时,又发现需要装配ComponentA

那么是否会引起Spring创建Bean失败呢?Spring是如何解决这种问题的?

Spring解决循环依赖的方式

为了解决这种问题,Spring引入了三级缓存,分别存放不同时期的Bean引用。

singletonObjects:一级缓存用来存放完全初始化完成的Bean。

earlySingletonObjects:二级缓存用来存放实例化,但初始化完成的Bean。

sngletonFactories:三级缓存用来存放ObjectFactory,ObjectFactory通常会持有已经实例化完成的Bean引用,并在getObject()时可能加工一下Bean,并返回Bean引用。

回到之前的问题:

  • Spring在创建ComponentA时,先实例化了ComponentA,并通过ObjectFactory持有着ComponentA的引用。在singletonFactories中(第三级缓存)添加缓存。
  • 然后开始装配ComponentA,发现需要依赖ComponentB,由于ComponentB在三级缓存中都找不到,于是转而开始创建ComponentB
  • 同样实例化了ComponentB,并在singletonFactories中添加缓存。
  • 开始对ComponentB进行装配,发现又需要ComponentA,Spring第二次去getBean(ComponentA)
  • 这次由于第三级缓存中已经存在了ComponentA的缓存,因此能够取到ComponentA实例的引用。同时,由于此次的获取,三级缓存的关系也发生了变化,第三级缓存singletonFacotries移除了关于ComponentA的引用,而是第二级缓存earlySingletonObjects会放入ComponentA的引用(因为此时已经通过ObjectFactory获取过ComponentA的实例了)。
  • ComponentB得到了ComponentA的引用,完成了装配。被添加到第一级缓存singletonObjects中,表示已经初始化完成,可以供其他Bean使用。
  • 回到第一个getBean(ComponentA)的地方,因为已经能够得到ComponentB的引用了,所以ComponentA也能被正常初始化完成。于是也被添加进了第一级缓存singletonObejcts中。

通过一个流程图帮助读者加深下印象:

无法解决的循环依赖的情形

上述循环依赖的情况是两个Bean在通过属性注入时产生的,而针对构造器注入的循环依赖Spring是无法解决的。

原因很简单,Spring之所以能解决循环依赖是因为它将Bean的创建过程分成了实例化和初始化两个阶段。实例化完成后Spring就通过三级缓存允许其他Bean先获取到该Bean的缓存。

而针对构造函数注入产生的循环依赖而言,由于在创建阶段就依赖了其他Bean,因此无法先创建实例供其他Bean引用。

疑惑

最后抛出一个问题:为什么解决循环依赖需要三级缓存的设计,个人认为二级缓存也能够解决循环依赖的问题,能否把第二级缓存和第三级缓存合并成一级?

希望有了解的同学可以帮我答疑解惑。

Spring.getBean()流程和循环依赖的解决的更多相关文章

  1. Spring IOC 容器源码分析 - 循环依赖的解决办法

    1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...

  2. Spring循环依赖的解决

    ## Spring循环依赖的解决 ### 什么是循环依赖 循环依赖,是依赖关系形成了一个圆环.比如:A对象有一个属性B,那么这时候我们称之为A依赖B,如果这时候B对象里面有一个属性A.那么这时候A和B ...

  3. 【Spring】Spring中的循环依赖及解决

    什么是循环依赖? 就是A对象依赖了B对象,B对象依赖了A对象. 比如: // A依赖了B class A{ public B b; } // B依赖了A class B{ public A a; } ...

  4. Spring源码解析——循环依赖的解决方案

    一.前言 承接<Spring源码解析--创建bean>.<Spring源码解析--创建bean的实例>,我们今天接着聊聊,循环依赖的解决方案,即创建bean的ObjectFac ...

  5. Spring 是怎么处理循环依赖的?

    Java语法中的循环依赖 首先看一个使用构造函数的循环依赖,如下: public class ObjectA { private ObjectB b; public ObjectA(ObjectB b ...

  6. Spring学习:简单实现一个依赖注入和循环依赖的解决

    依赖注入 什么是依赖注入 使用一个会创建和查找依赖对象的容器,让它负责供给对象. 当a对象需要b对象时,不再是使用new创建,而是从容器中获取,对象与对象之间是松散耦合的关系,有利于功能复用. 依赖: ...

  7. Spring GetBean流程

     第一节讲解Spring启动的时候说到,Spring内部先解析了所有的配置,加载所有的Bean定义后,再根据需要对Bean进行实例化和初始化.除开Spring自己主动新建的对象,第一次根据Bean定义 ...

  8. [跟我学spring学习笔记][DI循环依赖]

    循环依赖 什么是循环依赖? 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方. Spring容器循环依赖包括构造器循环依赖和setter循环依赖,那Spring容器如何解决循环依赖呢? ...

  9. Spring 使用@Async出现循环依赖Bean报错的解决方案

    初现端倪 Caused by:org.springframework.beans.factory.BeanCurrentlyInCreationException: Errorcreating bea ...

随机推荐

  1. vs整合MySQL和QT

    23:37:23 2019-08-12 尝试用vs写一个程序整合MySQL和QT 参考资料:https://blog.csdn.net/qq_35987486/article/details/8406 ...

  2. Yum 软件仓库配置

    Yum 软件仓库的作用是为了进一步简化 RPM 管理软件的难度以及自动分析 所需软件包及其依赖关系的技术. 可以把 Yum 想象成是一个硕大的软件仓库,里面保存有几乎所 有常用的工具 . 第1步:进入 ...

  3. var、let和const的区别详解

      let 和 const 是 ECMAScript6 新推出的特性,其中 let 是能够替代 var 的"标准",所以我们探讨 var.let 和 const 的区别,首先应该知 ...

  4. 微信小程序H5预览页面框架(二维码不隐藏)

    <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...

  5. Deep Dream模型与实现

    Deep Dream是谷歌公司在2015年公布的一项有趣的技术.在训练好的卷积神经网络中,只需要设定几个参数,就可以通过这项技术生成一张图像. 本文章的代码和图片都放在我的github上,想实现本文代 ...

  6. css--->圆角设置

    1.为元素添加四个相同的圆角: 语法结构:border-radius:r: r为圆角的半径大小 eg:如下样式,给元素添加四个圆角为10px   代码如下: <!DOCTYPE html> ...

  7. 小猪佩奇C代码实现

    // ASCII Peppa Pig by Milo Yip #include <stdio.h> #include <math.h> #include <stdlib. ...

  8. 深入了解CI/CD:工具、方法、环境、基础架构的全面指南

    本文来自Rancher Labs 持续集成和持续交付(CI/CD)是DevOps背后的助推力之一.如果你的企业正在考虑使用DevOps,那么CI/CD绝对是需要考虑的其中一部分.但是CI/CD到底意味 ...

  9. L21 Momentum RMSProp等优化方法

    airfoil4755 下载 链接:https://pan.baidu.com/s/1YEtNjJ0_G9eeH6A6vHXhnA 提取码:dwjq 11.6 Momentum 在 Section 1 ...

  10. L1-L11 jupter notebook 文件

    L1-L11 jupter notebook 文件下载地址 https://download.csdn.net/download/xiuyu1860/12157961 包括L12 Transforme ...