您的位置:首页 > 编程语言 > Java开发

spring循环依赖源码解决分析

2020-01-13 23:21 549 查看

Bean的生命周期以及三级缓存介绍

  • 总结
  • 描述

    循环依赖是我们在开发中会经常遇到的一种现象,beanA依赖于beanB,beanB又依赖于beanA,形成了一个循环

    像这样的问题spring提供了三级缓存解决非构造注入的解决方式,但是对于构造器注入的方式,还是会存在问题,原理如下。

    代码样例

    1.代码结构图

    2.InstantA.java

    import org.springframework.stereotype.Component;
    
    @Component
    public class InstantA {
    private InstantB ib;
    
    public InstantB getIb() {
    return ib;
    }
    
    public void setIb(InstantB ib) {
    this.ib = ib;
    }
    }

    3.InstantB.java

    import org.springframework.stereotype.Component;
    
    @Component
    public class InstantB {
    private InstantA ia;
    
    public InstantA getIa() {
    return ia;
    }
    
    public void setIa(InstantA ia) {
    this.ia = ia;
    }
    }

    4.spring.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--  非构造器注入 -->
    <bean id="ia" class="com.zk.test.InstantA">
    <property name="ib" ref="ib"></property>
    </bean>
    <bean id="ib" class="com.zk.test.InstantB">
    <property name="ia" ref="ia"></property>
    </bean>
    <!--  构造器注入 -->
    <!-- <bean id="ia" class="com.zk.test.InstantA">
    <constructor-arg name="ib" ref="ib"></constructor-arg>
    </bean>
    <bean id="ib" class="com.zk.test.InstantB">
    <constructor-arg name="ia" ref="ia"></constructor-arg>
    </bean> -->
    </beans>

    5.MainConfigure.java

    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.ImportResource;
    
    @Configuration
    @ImportResource(locations="classpath:spring.xml")
    @ComponentScan(basePackages= {"com.zk.test"})
    public class MainConfigure {
    
    }

    6.Main.java

    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class Main {
    
    public static void main(String[] args) {
    // TODO Auto-generated method stub
    //上下文 创建IOC容器
    AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext(MainConfigure.class);
    //获取bean  去容器的缓存中直接拿
    InstantA InstantA=(InstantA) ctx.getBean("ia");
    ctx.close();
    }
    
    }

    提供测试代码是方便自己debug查看。

    非构造器注入循环依赖

    在理解这一块之前需要先去看一下我之前写的bean的生命周期和三级缓存,看完这个会对下面bean创建的流程就有了解了。下面是beanA创建的时候如何去获取beanB

    由上图可以了解到,

    1. 在创建早期对象的时候是根据构造器去创建的,此时没有给早期对象beanA赋属性值
      2. 然后spring就把早期对象beanA放入了三级缓存之中,
      3. 继续往下走,就会给早期对象beanA赋属性值,此时就会去获取beanB,而beanB还没有被创建
      4. 调用getBean方法获取benaB,如果beanB不存在就会调用创建方法
      5. 去缓存中获取beanB,不存在调用创建方法
      6. 同理创建beanB的早期对象
      7. 然后spring就把早期对象beanB放入了三级缓存之中,
      8. 给早期对象beanB赋属性值,而此时就会去获取beanA
      9. 调用getBean获取beanA
      10. 可以在缓存中找到beanA,此时返回给beanB,beanB继续创建
      11. beanB创建结束返回给beanA
      12. beanA继续创建
      13. beanA创建结束

    以上就是创建的所有,从上述可以看到,需要先创建早期对象,并且放入三级缓存,在填充属性值的时候创建beanB,而如果是构造器方法注入的话,就需要在创建早期对象的时候就开始创建benaB,就会存在缓存中没有存入beanA的情况

    构造器注入循环依赖

    流程如下图

    1. 创建早期对象beanA的时候,就需要获取beanB
    2. 缓存中不存在beanB,开始创建beanB
    3. 创建beanB的早期对象,此时需要去获取beanA
    4. beanA在缓存中获取不到,开始获取beanB
    5. 然后就是2,3,4循环,最后就报错结束

    总结

    1.spring提供了三级缓存,用来解决非构造器注入循环依赖的问题
    2.在写代码的时候不要使用构造器注入的方式注入循环依赖的bean
    3.文章短。

    • 点赞
    • 收藏
    • 分享
    • 文章举报
    雀跃set 发布了57 篇原创文章 · 获赞 0 · 访问量 830 私信 关注
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: