java|Spring IOC 深入浅出


Spring IOC 深入浅出

    • Spring IoC基础
      • BeanFactory与ApplicationContext区别
      • 纯xml模式
      • xml与注解相结合模式
      • 纯注解模式
    • Spring IOC?级特性
      • lazy-Init 延迟加载
      • FactoryBean 和 BeanFactory
      • 后置处理器
        • BeanPostProcessor
        • BeanFactoryPostProcessor
    • Spring IoC容器初始化主体流程
      • Spring IoC的容器体系
      • Bean?命周期关键时机点
      • Spring IoC容器初始化主流程
    • BeanFactory创建流程
      • 获取BeanFactory?流程
      • BeanDefinition加载解析及注册?流程
    • Bean创建流程
    • lazy-init 延迟加载机制原理
    • Spring IoC循环依赖问题
      • 什么是循环依赖
      • 循环依赖处理机制

Spring IoC基础 java|Spring IOC 深入浅出
文章图片

BeanFactory与ApplicationContext区别
BeanFactory是Spring框架中IoC容器的顶层接?,它只是?来定义?些基础功能,定义?些基础规范,?ApplicationContext是它的?个?接?,所以ApplicationContext是具备BeanFactory提供的全部功能的。
通常,我们称BeanFactory为SpringIOC的基础容ApplicationContext是容器的?级接?,?BeanFactory要拥有更多的功能,?如说国际化?持和资源访问(xml,java配置类)等等

启动 IoC 容器的?式
  • Java环境下启动IoC容器 ClassPathXmlApplicationContext:从类的根路径下加载配置?件(推荐使?)
    FileSystemXmlApplicationContext:从磁盘路径上加载配置?件
    AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
  • Web环境下启动IoC容器
    从xml启动容器
    java|Spring IOC 深入浅出
    文章图片

    从配置类启动容器java|Spring IOC 深入浅出
    文章图片
    java|Spring IOC 深入浅出
    文章图片
纯xml模式
本部分内容我们不采???讲解知识点的?式,?是采?Spring IoC 纯 xml 模式改造我们前??写的IoC 和 AOP 实现,在改造的过程中,把各个知识点串起来。
  • xml ?件头java|Spring IOC 深入浅出
    文章图片

  • 实例化Bean的三种?式
    ?式?:使??参构造函数
    在默认情况下,它会通过反射调??参构造函数来创建对象。如果类中没有?参构造函数,将创建失败。java|Spring IOC 深入浅出
    文章图片

    ?式?:使?静态?法创建
    在实际开发中,我们使?的对象有些时候并不是直接通过构造函数就可以创建出来的,它可能在创建的过程 中会做很多额外的操作。此时会提供?个创建对象的?法,恰好这个?法是static修饰的?法,即是此种情况。
    例如,我们在做Jdbc操作时,会?到java.sql.Connection接?的实现类,如果是mysql数据库,那么?的就 是JDBC4Connection,但是我们不会去写 JDBC4Connection connection = new JDBC4Connection() ,因 为我们要注册驱动,还要提供URL和凭证信息,? DriverManager.getConnection ?法来获取连接。
    那么在实际开发中,尤其早期的项?没有使?Spring框架来管理对象的创建,但是在设计时使?了??模式 解耦,那么当接?spring之后,??类创建对象就具有和上述例?相同特征,即可采?此种?式配置。java|Spring IOC 深入浅出
    文章图片

    ?式三:使?实例化?法创建
    此种?式和上?静态?法创建其实类似,区别是?于获取对象的?法不再是static修饰的了,?是类中的? 个普通?法。此种?式?静态?法创建的使??率要??些。在早期开发的项?中,??类中的?法有可能是静态的,也有可能是?静态?法,当是?静态?法时,即可 采?下?的配置?式:java|Spring IOC 深入浅出
    文章图片

  • Bean的X及?命周期
    作?范围的改变
    在spring框架管理Bean对象的创建时,Bean对象默认都是单例的,但是它?持配置的?式改变作?范围。作?范围官?提供的说明如下图:java|Spring IOC 深入浅出
    文章图片
    在上图中提供的这些选项中,我们实际开发中?到最多的作?范围就是singleton(单例模式)和prototype(原型模式,也叫多例模式)。配置?式参考下?的代码:java|Spring IOC 深入浅出
    文章图片

    不同作?范围的?命周期
    单例模式:singleton
    对象出?:当创建容器时,对象就被创建了。
    对象活着:只要容器在,对象?直活着。
    对象死亡:当销毁容器时,对象就被销毁了。
    ?句话总结:单例模式的bean对象?命周期与容器相同。
    多例模式:prototype
    对象出?:当使?对象时,创建新的对象实例。
    对象活着:只要对象在使?中,就?直活着。
    对象死亡:当对象?时间不?时,被java的垃圾回收器回收了。
    ?句话总结:多例模式的bean对象,spring框架只负责创建,不负责销毁。
  • Bean标签属性
    在基于xml的IoC配置中,bean标签是最基础的标签。它表示了IoC容器中的?个对象。换句话说,如果?个对象想让spring管理,在XML的配置中都需要使?此标签配置,Bean标签的属性如下:
    id属性: ?于给bean提供?个唯?标识。在?个标签内部,标识必须唯?。
    class属性:?于指定创建Bean对象的全限定类名。
    name属性:?于给bean提供?个或多个名称。多个名称?空格分隔。
    factory-bean属性:?于指定创建当前bean对象的??bean的唯?标识。当指定了此属性之后,class属性失效。
    factory-method属性:?于指定创建当前bean对象的???法,如配合factory-bean属性使?,则class属性失效。如配合class属性使?,则?法必须是static的。
    scope属性:?于指定bean对象的作?范围。通常情况下就是singleton。当要?到多例模式时,可以配置为prototype。
    init-method属性:?于指定bean对象的初始化?法,此?法会在bean对象装配后调?。必须是?个?参?法。
    destory-method属性:?于指定bean对象的销毁?法,此?法会在bean对象销毁前执?。它只能为scope是singleton时起作?。
  • DI 依赖注?的xml配置
    依赖注?分类
    按照注?的?式分类
    构造函数注?:顾名思义,就是利?带参构造函数实现对类成员的数据赋值。
    set?法注?:它是通过类成员的set?法实现数据的注?。(使?最多的)
    按照注?的数据类型分类
    基本类型和String
    注?的数据类型是基本类型或者是字符串类型的数据。
    其他Bean类型
    注?的数据类型是对象类型,称为其他Bean的原因是,这个对象是要求出现在IoC容器中的。那么针对当前Bean来说,就是其他Bean了。
    复杂类型(集合类型)
    注?的数据类型是Aarry,List,Set,Map,Properties中的?种类型。
依赖注?的配置实现之构造函数注? 顾名思义,就是利?构造函数实现对类成员的赋值。它的使?要求是,类中提供的构造函数参数个数必须和配置的参数个数?致,且数据类型匹配。同时需要注意的是,当没有?参构造时,则必须提供构造函数参数的注?,否则Spring框架会报错。
java|Spring IOC 深入浅出
文章图片

在使?构造函数注?时,涉及的标签是 constructor-arg ,该标签有如下属性:
name:?于给构造函数中指定名称的参数赋值。
index:?于给构造函数中指定索引位置的参数赋值。
value:?于指定基本类型或者String类型的数据。
ref:?于指定其他Bean类型的数据。写的是其他bean的唯?标识。
  • 依赖注?的配置实现之set?法注?
顾名思义,就是利?字段的set?法实现赋值的注??式。此种?式在实际开发中是使?最多的注??式。
java|Spring IOC 深入浅出
文章图片

java|Spring IOC 深入浅出
文章图片

在使?set?法注?时,需要使? property 标签,该标签属性如下:
name:指定注?时调?的set?法名称。(注:不包含set这三个字?,druid连接池指定属性名称)
value:指定注?的数据。它?持基本类型和String类型。
ref:指定注?的数据。它?持其他bean类型。写的是其他bean的唯?标识。
复杂数据类型注? ?先,解释?下复杂类型数据,它指的是集合类型数据。集合分为两类,?类是List结构(数组结构),?类是Map接?(键值对) 。
接下来就是注?的?式的选择,只能在构造函数和set?法中选择,我们的示例选?set?法注?。
java|Spring IOC 深入浅出
文章图片

java|Spring IOC 深入浅出
文章图片
在List结构的集合数据注?时, array , list , set 这三个标签通?,另外注值的 value 标签内部可以直接写值,也可以使? bean 标签配置?个对象,或者? ref 标签引??个已经配合的bean的唯?标识。
在Map结构的集合数据注?时, map 标签使? entry ?标签实现数据注?, entry 标签可以使?key和value属性指定存?map中的数据。使?value-ref属性指定已经配置好的bean的引?。同时 entry 标签中也可以使? ref 标签,但是不能使? bean 标签。? property 标签中不能使? ref 或者 bean 标签引?对象
xml与注解相结合模式
注意:
1)实际企业开发中,纯xml模式使?已经很少了
2)引?注解功能,不需要引?额外的jar
3)xml+注解结合模式,xml?件依然存在,所以,spring IOC容器的启动仍然从加载xml开始
4)哪些bean的定义写在xml中,哪些bean的定义使?注解
第三?jar中的bean定义在xml,?如德鲁伊数据库连接池??开发的bean定义使?注解
  • xml中标签与注解的对应(IoC)
    java|Spring IOC 深入浅出
    文章图片

  • DI 依赖注?的注解实现?式
    @Autowired(推荐使?)
    @Autowired为Spring提供的注解,需要导?包
    org.springframework.beans.factory.annotation.Autowired。
    @Autowired采取的策略为按照类型注?。
    java|Spring IOC 深入浅出
    文章图片
    如上代码所示,这样装配回去spring容器中找到类型为AccountDao的类,然后将其注?进来。这样会产??个问题,当?个类型有多个bean值的时候,会造成?法选择具体注?哪?个的情况,这个时候我们需要配合着@Qualifier使?。
    @Qualifier告诉Spring具体去装配哪个对象。
    java|Spring IOC 深入浅出
    文章图片
    这个时候我们就可以通过类型和名称定位到我们想注?的对象。
    @Resource
    @Resource 注解由 J2EE 提供,需要导?包 javax.annotation.Resource。
    @Resource 默认按照 ByName ?动注?。
    java|Spring IOC 深入浅出
    文章图片

  • 如果同时指定了 name 和 type,则从Spring上下?中找到唯?匹配的bean进?装配,找不 到则抛出异常。
  • 如果指定了name,则从上下?中查找名称(id)匹配的bean进?装配,找不到则抛出异常。
  • 如果指定了type,则从上下?中找到类似匹配的唯?bean进?装配,找不到或是找到多个, 都会抛出异常。
  • 如果既没有指定name,?没有指定type,则?动按照byName?式进?装配;
    注意:
    @Resource 在 Jdk 11中已经移除,如果要使?,需要单独引?jar包
    java|Spring IOC 深入浅出
    文章图片

纯注解模式
改造xm+注解模式,将xml中遗留的内容全部以注解的形式迁移出去,最终删除xml,从Java配置类启动
对应注解
@Configuration 注解,表名当前类是?个配置类
@ComponentScan 注解,替代 context:component-scan
@PropertySource,引?外部属性配置?件
@Import 引?其他配置类
@Value 对变量赋值,可以直接赋值,也可以使? ${} 读取资源配置?件中的信息
@Bean 将?法返回对象加? SpringIOC 容器
Spring IOC?级特性 lazy-Init 延迟加载
Bean的延迟加载(延迟创建)
ApplicationContext 容器的默认?为是在启动服务器时将所有 singleton bean 提前进?实例化。提前实例化意味着作为初始化过程的?部分,ApplicationContext 实例会创建并配置所有的singleton
bean。
?如:java|Spring IOC 深入浅出
文章图片
lazy-init=“false”,?即加载,表示在spring启动时,?刻进?实例化。
如果不想让?个singleton bean 在 ApplicationContext实现初始化时被提前实例化,那么可以将bean设置为延迟实例化。
java|Spring IOC 深入浅出
文章图片
设置 lazy-init 为 true 的 bean 将不会在 ApplicationContext 启动时提前被实例化,?是第?次向容器通过 getBean 索取 bean 时实例化的。
如果?个设置了?即加载的 bean1,引?了?个延迟加载的 bean2 ,那么 bean1 在容器启动时被实例
化,? bean2 由于被 bean1 引?,所以也被实例化,这种情况也符合延时加载的 bean 在第?次调?时才被实例化的规则。也可以在容器层次中通过在 元素上使? “default-lazy-init” 属性来控制延时初始化。如下?配置:java|Spring IOC 深入浅出
文章图片
如果?个 bean 的 scope 属性为 scope=“pototype” 时,即使设置了 lazy-init=“false”,容器启动时也不会实例化bean,?是调? getBean ?法实例化的。
应?场景
(1)开启延迟加载?定程度提?容器启动和运转性能
(2)对于不常使?的 Bean 设置延迟加载,这样偶尔使?的时候再加载,不必要从?开始该 Bean 就占?资源
FactoryBean 和 BeanFactory
BeanFactory接?是容器的顶级接?,定义了容器的?些基础?为,负责?产和管理Bean的?个??,具体使?它下?的?接?类型,?如ApplicationContext;此处我们重点分析FactoryBean
Spring中Bean有两种,?种是普通Bean,?种是??Bean(FactoryBean),FactoryBean可以?成某?个类型的Bean实例(返回给我们),也就是说我们可以借助于它?定义Bean的创建过程。
Bean创建的三种?式中的静态?法和实例化?法和FactoryBean作?类似,FactoryBean使?较多,尤其在Spring框架?些组件中会使?,还有其他框架和Spring框架整合时使?java|Spring IOC 深入浅出
文章图片
Company类java|Spring IOC 深入浅出
文章图片
java|Spring IOC 深入浅出
文章图片
CompanyFactoryBean类java|Spring IOC 深入浅出
文章图片
java|Spring IOC 深入浅出
文章图片
xml配置java|Spring IOC 深入浅出
文章图片
测试,获取FactoryBean产?的对象java|Spring IOC 深入浅出
文章图片
测试,获取FactoryBean,需要在id之前添加“&”java|Spring IOC 深入浅出
文章图片

后置处理器
Spring提供了两种后处理bean的扩展接?,分别为BeanPostProcessor 和BeanFactoryPostProcessor,两者在使?上是有所区别的。
??初始化(BeanFactory)—> Bean对象
在BeanFactory初始化之后可以使?BeanFactoryPostProcessor进?后置处理做?些事情
在Bean对象实例化(并不是Bean的整个?命周期完成)之后可以使?BeanPostProcessor进?后置处理做?些事情
注意:对象不?定是springbean,?springbean?定是个对象
SpringBean的?命周期
BeanPostProcessor BeanPostProcessor是针对Bean级别的处理,可以针对某个具体的Bean.
java|Spring IOC 深入浅出
文章图片
该接?提供了两个?法,分别在Bean的初始化?法前和初始化?法后执?,具体这个初始化?法指的是什么?法,类似我们在定义bean时,定义了init-method所指定的?法
定义?个类实现了BeanPostProcessor,默认是会对整个Spring容器中所有的bean进?处理。如果要对具体的某个bean处理,可以通过?法参数判断,两个类型参数分别为Object和String,第?个参数是每个bean的实例,第?个参数是每个bean的name或者id属性的值。所以我们可以通过第?个参数,来判断我们将要处理的具体的bean。
注意:处理是发?在Spring容器的实例化和依赖注?之后。
BeanFactoryPostProcessor BeanFactory级别的处理,是针对整个Bean的??进?处理,典型应?:PropertyPlaceholderConfigurerjava|Spring IOC 深入浅出
文章图片
java|Spring IOC 深入浅出
文章图片
其中有个?法名为getBeanDefinition的?法,我们可以根据此?法,找到我们定义bean 的BeanDefinition对象。然后我们可以对定义的属性进?修改,以下是BeanDefinition中的?法
java|Spring IOC 深入浅出
文章图片
?法名字类似我们bean标签的属性,setBeanClassName对应bean标签中的class属性,所以当我们拿到BeanDefinition对象时,我们可以?动修改bean标签中所定义的属性值。
BeanDefinition对象:我们在 XML 中定义的 bean标签,Spring 解析 bean 标签成为?个 JavaBean,这个JavaBean 就是 BeanDefinition
注意:调? BeanFactoryPostProcessor ?法时,这时候bean还没有实例化,此时 bean 刚被解析成BeanDefinition对象
Spring IoC容器初始化主体流程 Spring IoC的容器体系
IoC容器是Spring的核?模块,是抽象了对象管理、依赖关系管理的框架解决?案。Spring 提供了很多的容器,其中 BeanFactory 是顶层容器(根容器),不能被实例化,它定义了所有 IoC 容器 必须遵从的?套原则,具体的容器实现可以增加额外的功能,?如我们常?到的ApplicationContext,其下更具体的实现如 ClassPathXmlApplicationContext 包含了解析 xml 等?系列的内容,AnnotationConfigApplicationContext 则是包含了注解解析等?系的内容。Spring IoC 容器继承体系?常聪明,需要使?哪个层次?哪个层次即可,不必使?功能??全的。
BeanFactory 顶级接??法栈如下
java|Spring IOC 深入浅出
文章图片
BeanFactory 容器继承体系java|Spring IOC 深入浅出
文章图片
通过其接?设计,我们可以看到我们?贯使?的 ApplicationContext 除了继承BeanFactory的?接?,还继承了ResourceLoader、MessageSource等接?,因此其提供的功能也就更丰富了。
下?我们以 ClasspathXmlApplicationContext 为例,深?源码说明 IoC 容器的初始化流程。
Bean?命周期关键时机点
思路:创建?个类 LagouBean ,让其实现?个特殊的接?,并分别在接?实现的构造器、接??法中断点,观察线程调?栈,分析出 Bean 对象创建和管理关键点的触发时机。
LagouBean类
java|Spring IOC 深入浅出
文章图片
java|Spring IOC 深入浅出
文章图片
BeanPostProcessor 接?实现类java|Spring IOC 深入浅出
文章图片
BeanFactoryPostProcessor 接?实现类java|Spring IOC 深入浅出
文章图片
applicationContext.xmljava|Spring IOC 深入浅出
文章图片
IoC 容器源码分析?例java|Spring IOC 深入浅出
文章图片
(1)分析 Bean 的创建是在容器初始化时还是在 getBean 时
java|Spring IOC 深入浅出
文章图片
根据断点调试,我们发现,在未设置延迟加载的前提下,Bean 的创建是在容器初始化过程中完成的。
(2)分析构造函数调?情况
java|Spring IOC 深入浅出
文章图片
java|Spring IOC 深入浅出
文章图片
通过如上观察,我们发现构造函数的调?时机在AbstractApplicationContext类refresh?法的finishBeanFactoryInitialization(beanFactory)处;
(3)分析 InitializingBean 之 afterPropertiesSet 初始化?法调?情况
java|Spring IOC 深入浅出
文章图片
通过如上观察,我们发现 InitializingBean中afterPropertiesSet ?法的调?时机也是在AbstractApplicationContext类refresh?法的finishBeanFactoryInitialization(beanFactory);
(4)分析BeanFactoryPostProcessor 初始化和调?情况
分别在构造函数、postProcessBeanFactory ?法处打断点,观察调?栈,发现
BeanFactoryPostProcessor 初始化在AbstractApplicationContext类refresh?法的
invokeBeanFactoryPostProcessors(beanFactory);
postProcessBeanFactory 调?在AbstractApplicationContext类refresh?法的
invokeBeanFactoryPostProcessors(beanFactory);
(5)分析 BeanPostProcessor 初始化和调?情况
分别在构造函数、postProcessBeanFactory ?法处打断点,观察调?栈,发现
BeanPostProcessor 初始化在AbstractApplicationContext类refresh?法的registerBeanPostProcessors(beanFactory);
postProcessBeforeInitialization 调?在AbstractApplicationContext类refresh?法的finishBeanFactoryInitialization(beanFactory);
postProcessAfterInitialization 调?在AbstractApplicationContext类refresh?法的
finishBeanFactoryInitialization(beanFactory);
(6)总结
根据上?的调试分析,我们发现 Bean对象创建的?个关键时机点代码层级的调?都在AbstractApplicationContext 类 的 refresh ?法中,可?这个?法对于Spring IoC 容器初始化来说相当关键,汇总如下:java|Spring IOC 深入浅出
文章图片

Spring IoC容器初始化主流程
由上分析可知,Spring IoC 容器初始化的关键环节就在 AbstractApplicationContext#refresh() ?法中,我们查看 refresh ?法来俯瞰容器创建的主体流程,主体流程下的具体?流程我们后?再来讨论。
java|Spring IOC 深入浅出
文章图片
java|Spring IOC 深入浅出
文章图片
java|Spring IOC 深入浅出
文章图片

BeanFactory创建流程 获取BeanFactory?流程
时序图如下
java|Spring IOC 深入浅出
文章图片

BeanDefinition加载解析及注册?流程
(1)该?流程涉及到如下?个关键步骤
Resource定位:指对BeanDefinition的资源定位过程。通俗讲就是找到定义Javabean信息的XML?件,并将其封装成Resource对象。
BeanDefinition载? :把?户定义好的Javabean表示为IoC容器内部的数据结构,这个容器内部的数据结构就是BeanDefinition。
注册BeanDefinition到 IoC 容器
(2)过程分析
Step 1:?流程??在AbstractRefreshableApplicationContext#refreshBeanFactory ?法中
java|Spring IOC 深入浅出
文章图片

Step 2:依次调?多个类的 loadBeanDefinitions ?法 —> AbstractXmlApplicationContext —>AbstractBeanDefinitionReader —> XmlBeanDefinitionReader ?直执?到XmlBeanDefinitionReader 的 doLoadBeanDefinitions ?法java|Spring IOC 深入浅出
文章图片
Step 3:我们重点观察XmlBeanDefinitionReader 类的registerBeanDefinitions ?法,期间产?了多次重载调?,我们定位到最后?个java|Spring IOC 深入浅出
文章图片
此处我们关注两个地?:?个createRederContext?法,?个是
DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions?法,先进?createRederContext ?法看看java|Spring IOC 深入浅出
文章图片
我们可以看到,此处 Spring ?先完成了NamespaceHandlerResolver 的初始化。
我们再进? registerBeanDefinitions ?法中追踪,调?了
DefaultBeanDefinitionDocumentReader#registerBeanDefinitions ?法
java|Spring IOC 深入浅出
文章图片
进? doRegisterBeanDefinitions ?法java|Spring IOC 深入浅出
文章图片
进? parseBeanDefinitions ?法java|Spring IOC 深入浅出
文章图片
进? parseDefaultElement ?法java|Spring IOC 深入浅出
文章图片
进? processBeanDefinition ?法java|Spring IOC 深入浅出
文章图片
?此,注册流程结束,我们发现,所谓的注册就是把封装的 XML 中定义的 Bean信息封装为BeanDefinition 对象之后放??个Map中,BeanFactory 是以 Map 的结构组织这些 BeanDefinition的。java|Spring IOC 深入浅出
文章图片
可以在DefaultListableBeanFactory中看到此Map的定义java|Spring IOC 深入浅出
文章图片
(3)时序图
java|Spring IOC 深入浅出
文章图片

Bean创建流程
  • 通过最开始的关键时机点分析,我们知道Bean创建?流程??在AbstractApplicationContext#refresh()?法的finishBeanFactoryInitialization(beanFactory)处java|Spring IOC 深入浅出
    文章图片

  • 进?finishBeanFactoryInitializationjava|Spring IOC 深入浅出
    文章图片

  • 继续进?DefaultListableBeanFactory类的preInstantiateSingletons?法,我们找到下?部分的代码,看到??Bean或者普通Bean,最终都是通过getBean的?法获取实例java|Spring IOC 深入浅出
    文章图片

  • 继续跟踪下去,我们进?到了AbstractBeanFactory类的doGetBean?法,这个?法中的代码很多,我们直接找到核?部分java|Spring IOC 深入浅出
    文章图片

  • 接着进?到AbstractAutowireCapableBeanFactory类的?法,找到以下代码部分java|Spring IOC 深入浅出
    文章图片

  • 进?doCreateBean?法看看,该?法我们关注两块重点区域java|Spring IOC 深入浅出
    文章图片

lazy-init 延迟加载机制原理
  • lazy-init 延迟加载机制分析
    普通 Bean 的初始化是在容器启动初始化阶段执?的,?被lazy-init=true修饰的 bean 则是在从容器?第?次进?context.getBean() 时进?触发。Spring 启动的时候会把所有bean信息(包括XML和注解)解析转化成Spring能够识别的BeanDefinition并存到Hashmap?供下?的初始化时?,然后对每个BeanDefinition 进?处理,如果是懒加载的则在容器初始化阶段不处理,其他的则在容器初始化阶段进?初始化并依赖注?。java|Spring IOC 深入浅出
    文章图片
    java|Spring IOC 深入浅出
    文章图片

  • 总结
    对于被修饰为lazy-init的bean Spring 容器初始化阶段不会进? init 并且依赖注?,当第?次进?getBean时候才进?初始化并依赖注?
    对于?懒加载的bean,getBean的时候会从缓存?头获取,因为容器初始化阶段 Bean 已经初始化完成并缓存了起来
Spring IoC循环依赖问题 什么是循环依赖
循环依赖其实就是循环引?,也就是两个或者两个以上的 Bean 互相持有对?,最终形成闭环。?如A依赖于B,B依赖于C,C?依赖于A。
java|Spring IOC 深入浅出
文章图片
注意,这?不是函数的循环调?,是对象的相互依赖关系。循环调?其实就是?个死循环,除?有终结条件。
Spring中循环依赖场景有:
  • 构造器的循环依赖(构造器注?)
  • Field 属性的循环依赖(set注?)
    其中,构造器的循环依赖问题?法解决,只能拋出 BeanCurrentlyInCreationException 异常,在解决属性循环依赖时,spring采?的是提前暴露对象的?法。
循环依赖处理机制
  • 单例 bean 构造器参数循环依赖(?法解决)
  • prototype 原型 bean循环依赖(?法解决)
    对于原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过setXxx?法产?循环依赖,Spring都 会直接报错处理。
    AbstractBeanFactory.doGetBean()?法:java|Spring IOC 深入浅出
    文章图片
    java|Spring IOC 深入浅出
    文章图片
    在获取bean之前如果这个原型bean正在被创建则直接抛出异常。原型bean在创建之前会进?标记这个beanName正在被创建,等创建结束之后会删除标记java|Spring IOC 深入浅出
    文章图片
    总结:Spring 不?持原型 bean 的循环依赖。
  • 【java|Spring IOC 深入浅出】单例bean通过setXxx或者@Autowired进?循环依赖
    Spring 的循环依赖的理论依据基于 Java 的引?传递,当获得对象的引?时,对象的属性是可以延后设置的,但是构造器必须是在获取引?之前
    Spring通过setXxx或者@Autowired?法解决循环依赖其实是通过提前暴露?个ObjectFactory对象来完成的,简单来说ClassA在调?构造器完成对象初始化之后,在调?ClassA的setClassB?法之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中。
    Spring容器初始化ClassA通过构造器初始化对象后提前暴露到Spring容器。java|Spring IOC 深入浅出
    文章图片
    ClassA调?setClassB?法,Spring?先尝试从容器中获取ClassB,此时ClassB不存在Spring容器中。
    Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中
    ClassB调?setClassA?法,Spring从容器中获取ClassA ,因为第?步中已经提前暴露了ClassA,因此可以获取到ClassA实例
    ClassA通过spring容器获取到ClassB,完成了对象初始化操作。
    这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题。

    推荐阅读