Spring循环依赖原理和Bean创建过程

举报
海风极客 发表于 2022/10/16 17:00:17 2022/10/16
【摘要】 Spring循环依赖 1.什么是循环依赖​ 循环依赖就是指Spring在创建Bean的过程中,一个Bean的属性依赖另一个Bean,而被依赖的Bean又依赖于当前Bean,如下图所示:代码如下:StudentService.javapublic class StudentService { private TeacherService teacherService; ...

Spring循环依赖

1.什么是循环依赖

​ 循环依赖就是指Spring在创建Bean的过程中,一个Bean的属性依赖另一个Bean,而被依赖的Bean又依赖于当前Bean,如下图所示:

在这里插入图片描述

代码如下:

StudentService.java

public class StudentService {

    private TeacherService teacherService;
    
    //因为要依赖注入,在这里我们先使用setter注入
    public void setTeacherService(TeacherService teacherService) {
        this.teacherService = teacherService;
    }

    static {
        System.out.println("创建StudentServiceBean...");
    }
}

TeacherService.java

public class TeacherService {

    private StudentService studentService;

    public void setStudentService(StudentService studentService) {
        this.studentService = studentService;
    }

    static {
        System.out.println("创建teacherServiceBean...");
    }
}

测试:

@Test
public void beanTest01() {
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    {
        RootBeanDefinition stu = new RootBeanDefinition(StudentService.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("teacherService", new RuntimeBeanReference("teacherService"));
        stu.setPropertyValues(new MutablePropertyValues()
                .addPropertyValues(map));
        factory.registerBeanDefinition("studentService", stu);
    }

    {
        RootBeanDefinition tea = new RootBeanDefinition(TeacherService.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("studentService", new RuntimeBeanReference("studentService"));
        tea.setPropertyValues(new MutablePropertyValues()
                .addPropertyValues(map));
        factory.registerBeanDefinition("teacherService", tea);
    }
    StudentService bean = factory.getBean(StudentService.class);
    System.out.println(bean);
}

debug一下看看效果:
在这里插入图片描述

显然,Spring创建的都是单例的Bean,循环依赖的完成也是由Spring自动进行的,下面我们从创建bean开始来了解Spring是如何解决循环依赖的。

2.Spring创建对象的过程

2.1 大致过程

大致过程无非就是这样:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w1kmGoO3-1624440558082)(Spring循环依赖.assets/image-20210623164832703.png)]

2.2 实际创建过程

详细过程就需要涉及到工厂模式、抽象工厂、单例池…
在这里插入图片描述

主要类的依赖图(全部为继承关系):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0O5f7YFA-1624440558088)(Spring循环依赖.assets/image-20210623165543072.png)]

  • 首先调用公开方法 DefaultListableBeanFactory.getBean(Class cls) 来获取 Bean 对象,通过一系列操作转化 class 对象为 beanName。
  • 得到 beanName 之后,调用 AbstractBeanFactory.getBean(String name, Class cls) , 在这个方法中又会调用受保护的 AbstractBeanFactory.doGetBean 方法
  • doGetBean 方法中,又会调用 DefaultSingletonBeanFactory.getSingleton(String beanName, ObjectFactory factory)
  • getSingleton 方法中,首先在单例池 DefaultSingletonBeanFactory.singletonObjects 查找是否已经存在 beanName 对应的单例对象。如果找不到,那会调用函数式接口 ObjectFactory 来创建一个对象,而这个对象工厂调用的函数是一个受保护的抽象方法 AbstractBeanFactory.crateBean(String beanName, RootBeanDefinition def, Object[] args)
  • AbstractAutowireCapableBeanFactory 实现了超类的 createBean 方法, 并在 createBean 方法中调用了受保护的方法 AbstractAutowireCapableBeanFactory.doCreateBean
  • doCreateBean方法中,还会调用 AbstractAutowireCapableBeanFactory.populateBean 来填充属性。
  • populateBean 填充属性时,还可能遇上没有创建过的对象,因此还可能递归调用 AbstractBeanFactory.getBean(String beanName)

AbstractAutowireCapableBeanFactory 负责了实例化 Bean,填充属性,后置处理的任务,DefaultSingletonBeanRegistry 负责了单例池的维护工作

3.Spring是如何解决循环依赖的

在这里插入图片描述

Spring 就是靠获取未完成的Bean对象来填充属性,解决循环依赖的。(这里使用到了三级缓存)

基本过程:

(1)通过getSingleton()时添加,Bean创建状态

(2)实例化Bean之后添加ObjectFactory至工厂池

(3)获取创建状态的Bean时,会先通过ObjectFactory获取Bean,然后添加到半成品池earlySingletonObjects

(4)对象完成创建后会添加到单例池

为什么不直接把对象放入 earlySingletonObjects,而是先放入 singletonFactories 中?
创建对象是需要消耗性能的,假如创建没有循环依赖的对象,是不需要创建未完成的Bean对象的,所以不是直接创建未完成对象放入未完成单例集 earlySingletonObjects,而是把未完成单例工厂放入 singletonFactories
比如以下这段代码,在运行时就不会调用 getSingleton 中的 ObjectFactory.getObject() 方法来创建未完成 Bean 对象。

4.什么情况下不能解决循环依赖

在Bean是多例的状态下不能解决循环依赖(因为按理来说要创建无数个Bean)

试一下:

@Test
public void beanTest01() {
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    {
        RootBeanDefinition stu = new RootBeanDefinition(StudentService.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("teacherService", new RuntimeBeanReference("teacherService"));
        stu.setPropertyValues(new MutablePropertyValues()
                .addPropertyValues(map));
        stu.setScope("prototype");  //将StudentService设置为多例模式
        factory.registerBeanDefinition("studentService", stu);
    }

    {
        RootBeanDefinition tea = new RootBeanDefinition(TeacherService.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("studentService", new RuntimeBeanReference("studentService"));
        tea.setPropertyValues(new MutablePropertyValues()
                .addPropertyValues(map));
        factory.registerBeanDefinition("teacherService", tea);
    }
    StudentService bean = factory.getBean(StudentService.class);
    System.out.println(bean);
}

报错:

在这里插入图片描述
参考文章:https://www.cnblogs.com/kendoziyu/p/spring-create-singleton-bean.html

PS:有些地方可能解释的不太清楚,欢迎读者提出建议

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。