1、背景
最近自己在尝试写一个简易的IOC容器,让自己更好的理解Spring的IOC原理。
当然我这里并没有参考任何Spring的源码(之后肯定会的!!!),而是参考敖丙写的文章进行的学习(链接放在文末)。
当我实现了简易的实例化、依赖注入功能后,突然想到了循环依赖问题,久闻大名,一直未去深入了解,于是决定在今天去揭开Spring解决循环依赖方案的面纱,一探究竟!!!
2、循环依赖是什么?
简单来说,循环依赖就是两个Bean对象的属性值互相为对方,再结合Spring中Bean的生命周期就非常好理解了。这里不过多展开,放一张图片让大家看一下(图源放在文末)。
如果还是不能理解,我文末会放一个链接,大家可以去看一下,同时也是上面的图源链接。
3、Spring是怎么发现循环依赖的?
没错,想要解决循环依赖,那就得先发现循环依赖,这里需要结合Spring两个的行为。
(1)Spring在创建bean之前会先去二级缓存中查找是否存在对应的bean
(2)Spring会把实例化完成,但还未完成依赖注入的bean放入的二级缓存中
结合以上两个行为,当Spring在创建bean之前,在二级缓存中查找到存在对应的bean,那么就代表存在循环依赖了。
4、Spring是怎么解决循环依赖的?
循环依赖是因为依赖注入阶段无法获取到只完成了实例化的bean对象,所以Spring就加了一个二级缓存,用于存放只完成了实例化的bean对象,提前暴露bean。
当发生循环原来时,Spring会把只完成实例化,但还未完成依赖注入的原始bean注入到目标bean中,以此来解决循环依赖。
PS:以上方案只能解决字段注入方式产生的循环依赖,当bean之间的循环依赖是构造器方法方式产生的时,此方案是无法解决的。
5、代码实现
以下是我自己的IOC容器中,解决循环依赖的二级缓存应用,非常简单,供大家参考。
/**
* 创建Bean并填充属性
* @param clazz
* @return
*/
private static Object doCreateBean(String beanName,Class<?> clazz) throws InstantiationException, IllegalAccessException {
// 1、使用无参构造方法实例化Bean
Object bean = clazz.newInstance();
// 1.1、把(完成实例化,但未属性赋值的bean)放入二级缓存,提前暴露bean
// 这一步,是为了解决循环依赖问题,也用于发现循环依赖问题
beanCacheMap.put(beanName,bean);
// 2、为bean填充属性(依赖注入)
populateBean(bean,clazz);
// 3、当前Bean完成依赖注入阶段,移除二级缓存中的bean
// 保证循环依赖的发现机制正常运转
beanCacheMap.remove(beanName);
return bean;
}
6、总结
Spring的设计总是让我惊叹,简直是编程界的艺术品,之后也计划对Spring进行深入的学习,期待有更多的内容分享给大家!!!加油加油!!!