写点什么

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

作者:Barry Yan
  • 2022 年 10 月 07 日
    北京
  • 本文字数:2647 字

    阅读完需:约 9 分钟

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

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..."); }}
复制代码


测试:


@Testpublic 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 大致过程

大致过程无非就是这样:


2.2 实际创建过程

详细过程就需要涉及到工厂模式、抽象工厂、单例池.......



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



  • 首先调用公开方法 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)


试一下:


@Testpublic 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:有些地方可能解释的不太清楚,欢迎读者提出建议

发布于: 刚刚阅读数: 3
用户头像

Barry Yan

关注

做兴趣使然的Hero 2021.01.14 加入

Just do it.

评论

发布
暂无评论
Spring循环依赖原理和Bean创建过程_10月月更_Barry Yan_InfoQ写作社区