Bean的生命周期以及三级缓存介绍
描述
循环依赖是我们在开发中会经常遇到的一种现象,beanA依赖于beanB,beanB又依赖于beanA,形成了一个循环
像这样的问题spring提供了三级缓存解决非构造注入的解决方式,但是对于构造器注入的方式,还是会存在问题,原理如下。
代码样例
1.代码结构图
2.InstantA.java
import org.springframework.stereotype.Component;
@Component
public class InstantA {
private InstantB ib;
public InstantB getIb() {
return ib;
}
public void setIb(InstantB ib) {
this.ib = ib;
}
}
3.InstantB.java
import org.springframework.stereotype.Component;
@Component
public class InstantB {
private InstantA ia;
public InstantA getIa() {
return ia;
}
public void setIa(InstantA ia) {
this.ia = ia;
}
}
4.spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 非构造器注入 -->
<bean id="ia" class="com.zk.test.InstantA">
<property name="ib" ref="ib"></property>
</bean>
<bean id="ib" class="com.zk.test.InstantB">
<property name="ia" ref="ia"></property>
</bean>
<!-- 构造器注入 -->
<!-- <bean id="ia" class="com.zk.test.InstantA">
<constructor-arg name="ib" ref="ib"></constructor-arg>
</bean>
<bean id="ib" class="com.zk.test.InstantB">
<constructor-arg name="ia" ref="ia"></constructor-arg>
</bean> -->
</beans>
5.MainConfigure.java
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
@Configuration
@ImportResource(locations="classpath:spring.xml")
@ComponentScan(basePackages= {"com.zk.test"})
public class MainConfigure {
}
6.Main.java
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
//上下文 创建IOC容器
AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext(MainConfigure.class);
//获取bean 去容器的缓存中直接拿
InstantA InstantA=(InstantA) ctx.getBean("ia");
ctx.close();
}
}
提供测试代码是方便自己debug查看。
非构造器注入循环依赖
在理解这一块之前需要先去看一下我之前写的bean的生命周期和三级缓存,看完这个会对下面bean创建的流程就有了解了。下面是beanA创建的时候如何去获取beanB
由上图可以了解到,
- 在创建早期对象的时候是根据构造器去创建的,此时没有给早期对象beanA赋属性值
2. 然后spring就把早期对象beanA放入了三级缓存之中,
3. 继续往下走,就会给早期对象beanA赋属性值,此时就会去获取beanB,而beanB还没有被创建
4. 调用getBean方法获取benaB,如果beanB不存在就会调用创建方法
5. 去缓存中获取beanB,不存在调用创建方法
6. 同理创建beanB的早期对象
7. 然后spring就把早期对象beanB放入了三级缓存之中,
8. 给早期对象beanB赋属性值,而此时就会去获取beanA
9. 调用getBean获取beanA
10. 可以在缓存中找到beanA,此时返回给beanB,beanB继续创建
11. beanB创建结束返回给beanA
12. beanA继续创建
13. beanA创建结束
以上就是创建的所有,从上述可以看到,需要先创建早期对象,并且放入三级缓存,在填充属性值的时候创建beanB,而如果是构造器方法注入的话,就需要在创建早期对象的时候就开始创建benaB,就会存在缓存中没有存入beanA的情况
构造器注入循环依赖
流程如下图
- 创建早期对象beanA的时候,就需要获取beanB
- 缓存中不存在beanB,开始创建beanB
- 创建beanB的早期对象,此时需要去获取beanA
- beanA在缓存中获取不到,开始获取beanB
- 然后就是2,3,4循环,最后就报错结束
总结
1.spring提供了三级缓存,用来解决非构造器注入循环依赖的问题
2.在写代码的时候不要使用构造器注入的方式注入循环依赖的bean
3.文章短。