026:SpringBean循环依赖问题解决方案
1 SpringBean的循环依赖任务安排
课程内容:
- 什么是Spring的循环依赖?循环依赖会存在那些问题
- Spring循环依赖在单例与多例的情况下有那些区别
- 使用三级缓存解决Spring循环依赖问题
2 SpringBean的循环依赖基本概念
Spring核心:SpringIOC和AOP概念、依赖注入(赋值)
什么是Spring循环依赖问题
A 对象中的属性B依赖于B对象,B对象中属性A依赖A对象
注意只有在bean对象是单例的情况下,Spring才会去解决循环依赖问题(三级缓存);
bean对象如果是多例的情况,因为设置注入的多例对象不明确,就会产生循环依赖问题(不断死循环创建对象,程序抛出异常)。
代码验证单例/多例情形循环依赖问题
@Configuration
@ComponentScan("com.mayikt.service")
public class MyConfig {
}
@Service
//@Scope("prototype")
public class AService {
// AService创建的时候,要等BService先创建
@Autowired
public BService bService;
}
@Service
//@Scope("prototype")
public class BService {
@Autowired
public AService aService;
}
public class SpringApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
AService aService = annotationConfigApplicationContext.getBean("AService", AService.class);
BService bService = annotationConfigApplicationContext.getBean("BService", BService.class);
}
}
运行结果:
放开多例注释,运行结果:
3 SpringBean多例如何解决循环依赖问题
思考问题:如果项目对象必须要是多例且必须要循环引用,如何解决?
明确指定引用哪个对象
@Service
@Scope("prototype")
public class AService {
// AService创建的时候,要等BService先创建
// @Autowired
public BService bService;
public void setBService(BService bService) {
this.bService = bService;
}
}
@Service
@Scope("prototype")
public class BService {
// @Autowired
public AService aService;
public void setAService(AService aService) {
this.aService = aService;
}
}
public class SpringApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
AService aService = annotationConfigApplicationContext.getBean("AService", AService.class);
BService bService = annotationConfigApplicationContext.getBean("BService", BService.class);
aService.setBService(bService);
bService.setAService(aService);
}
}
运行结果:
4 SpringBean循环依赖三级缓存概念
分析spring单例底层如何解决循环依赖问题
单例对象在ioc容器被加载的时候创建,多例的情况下是在调用的时候创建。
SpringBean中 AService对象被创建流程步骤:
- doGetBean创建bean对象
- getSingleton (beanName) 获取缓存对象
singletonObjects 一级缓存完整对象(对象已经实例化成功并且所有属性都已经赋值)
earlySingletonObjects 二级缓存,缓存婴儿对象
singletonFactories 三级缓存,存放婴儿对象
5 SpringBean循环依赖源码分析
理解概念:
完整对象:该对象实例化完成并且所有的属性已经赋值。
婴儿对象(提前对象):已经实例化完成但是属性没有赋值。
- 走doGetBean方法的else分支,调用getSingleton(String beanName, ObjectFactory<?> singletonFactory)
this.singletonsCurrentlyInCreation.add(beanName) 添加标记,表示该对象已经开始创建
- createBean() →doCreateBean
- addSingletonFactory将婴儿对象(不完整对象,只是实例化完成但是属性没有赋值) 存放三级缓存中;
- A对象已经存放到三级缓存中,开始给对象属性赋值的时候需要创建B对象。创建B对象的时候调用getBean方法,也把B对象放入三级缓存中,同样给B属性赋值的时候需要依赖A。此时从三级缓存中获取A(不完整)给B赋值,B变成完全对象,再给A赋值A也变成完整对象。
6 Spring循环依赖Bean对象引用问题
public class SpringApp {
public static void main(String[] args) {
// 缓存婴儿对象
HashMap<String, Object> stringObjectHashMap = new HashMap<>();
stringObjectHashMap.put("aService",new AService());
stringObjectHashMap.put("bService",new BService());
// 从缓存中查询到赋值
AService aService = (AService) stringObjectHashMap.get("aService");
BService bService = (BService) stringObjectHashMap.get("bService");
// java的引用传递
aService.setBService(bService);
bService.setAService(aService);
}
}