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 大致过程
大致过程无非就是这样:
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)
试一下:
@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); } 复制代码
报错:
参考文章:www.cnblogs.com/kendoziyu/p…