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...");
}
}
测试:
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)
试一下:
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);
}
报错:
参考文章:
PS:有些地方可能解释的不太清楚,欢迎读者提出建议