前面我们已经详细的了解了实例化Bean的全部过程,而本文则是基于前面的3篇博客的基础之上进行进一步分析:
实例化BeanDefinition:Spring之基于注解方式实例化BeanDefinition(1)_chen_yao_kerr的博客-CSDN博客
实例化的Bean:Spring之实例化Bean(2)_chen_yao_kerr的博客-CSDN博客
@Resource和@Autowired注入Bean:Spring之实例化Bean _ @Resource和@Autowired实现原理(3)_chen_yao_kerr的博客-CSDN博客
其实,掌握了以上3篇关于实例化Bean和注入Bean的过程,循环依赖就变得简单了:下面看一下今天的测试类:
A:
package com.xiangxue.jack.CirDepend;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
@Component
public class A {
String name;
@Resource
B b;
public void typeName() {
System.out.println(b.name);
}
@PostConstruct
public void init() {
name = "I am A";
System.out.println("A 开始实例化name :" + name);
}
}
B:
package com.xiangxue.jack.CirDepend;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
@Component
public class B {
String name;
@Resource
A a;
public void typeName() {
System.out.println(a.name);
}
@PostConstruct
public void init() {
name = "I am B";
System.out.println("B 开始实例化name :" + name);
}
}
测试入口类:
package com.xiangxue.jack;
import com.xiangxue.jack.bean.Dao;
import com.xiangxue.jack.bean.MyTestBean2;
import com.xiangxue.jack.postProcessor.Dao2;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
//@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring.xml"})
public class MyTest {
@Autowired
private ApplicationContext applicationContext;
@Test
public void myBean4() {
//测试循环依赖
System.out.println("=================开始我们的循环依赖=======================");
applicationContext = new ClassPathXmlApplicationContext("spring.xml");
System.out.println("=================开始调用 b 对象========================");
}
}
其实,整个循环依赖的流程就可以简单概括为10个步骤:
A类 | B类 | A类 |
1. 我们调用getBean方法实例化A对象,完成后放入第3级缓存中,最后调用populatedBean方法进行依赖注入B对象。上一篇已经说过,此时会调用getBean方法实例化B对象的过程 | ||
2. 开始实例化B对象的过程, 实例化完成后,我们将B对象放入第3级缓存中。最后调用populatedBean方法进行依赖注入A对象。再次调用getBean方法实例化A对象 | ||
3. 开始实例化A对象。此时,我们发现在第3级缓存找找到了实例化A对象的ObjectFactory对象,因此不需要再次走全新的实例化新的A对象流程,直接通过objectFactory的getObject方法获取A对象 | ||
4. 此时,我们获取到了A的实例,将A对象实例注入到B对象中。 | ||
5. B类已经成功将A类实例注入 | ||
6.调用initializeBean方法,完成B类的初始化 | ||
7.调用addSingleton将初始化后的Bean放入1级缓存中,删除2、3级缓存。 返回 | ||
8,此时返回到A注入B对象的代码处. 我们获取到了B的实例,将B注入到A中 | ||
9. 调用initializeBean方法,完成A类的初始化 | ||
10。 调用addSingleton将初始化后的A实例放入1级缓存中,删除2、3级缓存。 |
此时,通过属性注入方式的循环依赖就结束了。一句话概括2个类的循环依赖过程。就是3次调用getBean方法的过程。下面看一下调用的全流程图:
最后需要强调一下,spring是不支持通过构造方法注入形式的循环依赖的。
A类:
package com.xiangxue.jack.CirDepend;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class A {
String name;
//@Resource
B b;
@Autowired
A(B b) {
System.out.println("测试构造方式支持循环依赖");
this.b =b;
}
public void typeName() {
System.out.println(b.name);
}
@PostConstruct
public void init() {
name = "I am A";
System.out.println("A 开始实例化name :" + name);
}
}
B类:
package com.xiangxue.jack.CirDepend;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class B {
String name;
//@Resource
A a;
@Autowired
B(A a){
System.out.println("测试构造方式支持循环依赖");
this.a = a;
}
public void typeName() {
System.out.println(a.name);
}
@PostConstruct
public void init() {
name = "I am B";
System.out.println("B 开始实例化name :" + name);
}
}
如果想要通过构造函数的形式实现循环依赖,在实例化bean的时候,我们会直接抛异常信息:
A类 | B类 | A类 |
1. 我们调用getBean方法实例化A对象,先从缓存中拿,第一次进来拿不到,所以把当前A对象的BeanName放入到singletonsCurrentlyInCreation集合中,便是此实例正在创建。 此时走有参构造函数实例化bean方法。获取到要注入的B对象 | ||
2. 开始实例化B对象的过程, 我们调用getBean方法实例化A对象,先从缓存中拿,第一次进来拿不到,所以把当前B对象的BeanName放入到singletonsCurrentlyInCreation集合中,便是此实例正在创建。此时走有参构造函数实例化bean方法。获取到要注入的A对象 | ||
3. 我们调用getBean方法实例化A对象。先从缓存中拿,此时我们在集合singletonsCurrentlyInCreation中找到了A的beanName,也就是说A的实例之前就一直在创建中了,所以直接抛异常 |
也就是说,我们实例化Bean完成以后,都会放入缓存中。而通过构造方法注入的形式实现循环依赖,一直就没有走到将实例化好的Bean放入缓存的那一步。