Spring循环依赖的原理解析
1、什么是循环依赖?
我们使用Spring的时候,在一个对象中注入另一个对象,但是另外的一个对象中也包含该对象。如图:
在Student中包含了teacher的一个属性;
在Teacher中包含有student的属性。这样就形成了一个循环依赖。
2、代码描述
xml配置文件
testCycle.java
private static void testCycle(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("cycle.xml");
Teacher teacher = applicationContext.getBean(Teacher.class);
System.out.println(teacher);
Student student = applicationContext.getBean(Student.class);
System.out.println(student);
}
public static void main(String[] args) {
testCycle();
}
Student.java
public class Student {
private Teacher teacher;
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
Teacher.java
public class Teacher {
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
}
3、 测试结果
此处输出的teacher中包含有student对象,student对象中也包含有teacher对象,且包含的对象都是不为null的。
4、为什么能够循环依赖解释
先给出一张图
在Spring创建bean的时候肯定也是一个一个去创建的。首先肯定会先去走一个(Teacher/Student)生命周期。这里以Teacher为例,当Spring去getBean(teacher)的时候,首先会去容器中获取,获取不到就会去创建teacher,当teacher创建完成后,会给teacher的属性(student)赋值,实际上容器中没有student对象,这时候也会去创建student对象,当student创建的时候会去给student中的teacher属性赋值,teacher之前已经创建过了,此时去getBean(teacher)是能够拿到的(注意:此时的teacher中student属性并没有赋值),这样student就创建完成了,那么就会回到teacher的student属性赋值的步骤,此时student已经创建是可以用getBean()拿到的,这样teacher对象就创建完毕了。然后回到第一步去创建student对象,这里student对象在创建teacher的时候就已经创建,可以直接使用getBean()获取到。给student中的属性赋值的时候也是一样,能够直接获取到teacher。自此循环依赖就已经结束了。
5、疑问
- 当我在给Teacher属性student的赋值的时候是怎么去getBean()的?
- 当给student中属性teacher赋值的时候getBean()为什么能够取到teacher?
- 为什么获取到的teacher属性是为完成注入的?
6、源码解释
整体的方法线
先看看源码:
getBean()->doGetBean()
getBean() -> doGetBean() 实际上是 doGetBean 再去获取bean对象
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
throws BeansException {
return doGetBean(name, requiredType, args, false);
}
/**
* Return an instance, which may be shared or independent, of the specified bean.
* 返回指定 bean 的一个实例,该实例可以是共享的,也可以是独立的。
*/
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// 转换beanName,FactoryBean的情况下beanName为&beanName,这里就是去掉&符号
String beanName = transformedBeanName(name);
Object beanInstance;
// Eagerly check singleton cache for manually registered singletons.
// 急切检查单例缓存从手动创建的单例中,获取bean判断是否存在当前beanName的bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
... 省略代码...
}
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
// 没有获取到,如果已经创建bean的实例,我们在一个循环引用中。当前的bean是否为正在创建中
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
// 检查该工厂中是否存在bean的定义
BeanFactory parentBeanFactory = getParentBeanFactory();
... 省略代码...
if (!typeCheckOnly) {
// 标记bean已经创建,正在创建
markBeanAsCreated(beanName);
}
StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
.tag("beanName", name);
try {
if (requiredType != null) {
beanCreation.tag("beanType", requiredType::toString);
}
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// 保证当前的bean所依赖的bean已经初始化
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep +