spring 源码探索--单例bean解决循环依赖问题
来源:互联网 发布:卖淘宝小号的网站 编辑:程序博客网 时间:2024/05/24 08:32
spring 中循环依赖问题:
ItemA 依赖ItemB,ItemB依赖ItemC,ItemC依赖ItemA,这就造成了循环依赖。
循环依赖有两种实现方式:构造函数,setter注入
单例模式
- 构造函数
public ItemA(ItemB itemB){ this.itemB = itemB;}
这种情况造成的循环依赖在spring中是无法解决的,只能报BeanCurrentlyInCreationException
。
在spring中,使用类的构造的函数实例化bean之前,即singletonObject = singletonFactory.getObject();
调用这个之前,会判断是否当前的bean是否正在创建,如果当前bean正在创建,则会报异常。如果不正在创建,则加入创建bean的池中进行标记。
- setter注入
即通过getter/setter方法注入bean.
这种情况可以解决,通过当实例化完bean,还没完成初始化时,提前暴露出一个ObjectFactory,这个工厂正好返回的是刚刚完成实例化的bean.
暴露ObjectFactory工厂
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } });}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }}
首先我们需要先了解几个bean的缓存池的作用
1. singletonObjects :BeanName和创建bean实例之间的关系(注:不是最终返回的bean,可能是FatoryBean)
2. singletonFactores: BeanName和创建bean的工厂
3. earlySingletonObjects: BeanName和创建bean实例之间的关系,与1有所不同,bean实例还处于创建的过程中,可以通过getBean获取,用于检测循环引用。(ps:这个缓存池与2是互斥的)
4. registeredSingletons:用来保存所有已经注册的bean.
到了这里,已经暴露了正在创建中的bean的ObjectFactory。
接下来就来到了初始化bean,注入相关的依赖 populateBean(beanName, mbd, instanceWrapper);
在这里面会注入依赖的bean,当有循环引用出现,一定有一个终止的条件才能解决循环依赖,就像递归一样。
在最开始加载bean的时候,spring首先从缓存中读取bean.
protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null);}
如果能在这几个缓存池拿到循环依赖的bean,那么循环依赖就解决了。
A -> B -> C -> A
假如一开始加载A对象,现在假如到了创建A的依赖C这个地方,那A现在的状态是刚刚完成构造函数的实例化,准备进行完成注入其他bean。这个时候创建他的ObjectFactory也已经加入到了singletonFactores
,C这个时候去注入A,通过上面的getSingleton
方法即可拿到刚刚实例化完的A对象。最后回到A对象,A继续完成其他属性的注入,此时C引用的A已经不再是刚刚实例化完的A对象了。整个依赖完成。
假设在上面的getSingleton
方法上没有得到bean A,那异常BeanCurrentlyInCreationException
会在再次创建bean A之前发生。
有一个正在创建bean池,会记录当前创建中的bean。
protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }}
原型模式
无法解决循环依赖,因为spring容器不缓存prototype作用域的bean.
- spring 源码探索--单例bean解决循环依赖问题
- 从spring源码角度分析循环依赖bean的组装
- Spring源码学习--Bean对象循环依赖问题解决(四)
- Spring-bean的循环依赖以及解决方式
- Spring Bean 循环依赖解决方案
- Spring Bean 循环依赖解决方案
- spring 源码-循环依赖
- spring循环依赖问题
- Spring源码解析 ---- 循环依赖
- spring源码分析 循环依赖
- Spring如何解决循环依赖
- Spring如何解决循环依赖
- Spring如何解决循环依赖
- spring 源码探索--bean加载准备阶段
- spring bean循环引用问题
- spring源码解读 Bean的依赖注入
- Spring单例Bean
- 解决requirejs循环依赖问题
- git安装
- MySQL数据库5.X版本基本手工注入总结
- UVa 156
- mui:文件上传
- js内存泄漏常见的四种情况
- spring 源码探索--单例bean解决循环依赖问题
- Android视频框架 Vitamio 打造自己的万能播放器
- C++中substr函数的用法
- BITCS2016程序设计 | 21. 穿越矩阵
- NOIP2016 提高组 爆炸记
- 快速幂取模
- 《失控》讨论会感触
- 视频框架Vitamio学习
- 链表排序之冒泡法---简易篇