spring 源码探索--单例bean解决循环依赖问题
2016-11-26 17:45
896 查看
spring 中循环依赖问题:
ItemA 依赖ItemB,ItemB依赖ItemC,ItemC依赖ItemA,这就造成了循环依赖。
循环依赖有两种实现方式:构造函数,setter注入
这种情况造成的循环依赖在spring中是无法解决的,只能报
在spring中,使用类的构造的函数实例化bean之前,即
setter注入
即通过getter/setter方法注入bean.
这种情况可以解决,通过当实例化完bean,还没完成初始化时,提前暴露出一个ObjectFactory,这个工厂正好返回的是刚刚完成实例化的bean.
暴露ObjectFactory工厂
首先我们需要先了解几个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,注入相关的依赖
在这里面会注入依赖的bean,当有循环引用出现,一定有一个终止的条件才能解决循环依赖,就像递归一样。
在最开始加载bean的时候,spring首先从缓存中读取bean.
如果能在这几个缓存池拿到循环依赖的bean,那么循环依赖就解决了。
A -> B -> C -> A
假如一开始加载A对象,现在假如到了创建A的依赖C这个地方,那A现在的状态是刚刚完成构造函数的实例化,准备进行完成注入其他bean。这个时候创建他的ObjectFactory也已经加入到了
假设在上面的
有一个正在创建bean池,会记录当前创建中的bean。
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 4.2 方法注入解决单例Bean的原型Bean依赖问题
- 从spring源码角度分析循环依赖bean的组装
- 解决spring循环依赖的问题:has been injected into other beans
- 解决spring循环依赖的问题:has been injected into other beans
- Spring源码-IOC容器(六)-bean的循环依赖
- Spring中的循环依赖问题介绍及解决方法
- 解决Spring中singleton的Bean依赖于prototype的Bean的问题
- Spring-bean的循环依赖以及解决方式
- Spring-bean的循环依赖以及解决方式
- 解决Spring中singleton的Bean依赖于prototype的Bean的问题
- 转--解决Spring中singleton的Bean依赖于prototype的Bean的问题
- Spring源码解析笔记5——循环依赖的解决
- Spring中的循环依赖问题介绍及解决方法
- Spring - Bean 循环依赖问题
- Spring源码阅读-BeanFactory-循环依赖
- 详解Spring-bean的循环依赖以及解决方式
- Spring管理的Bean对象默认是单例模式,怎么解决线程安全问题
- 解决spring配置中的bean类型的问题:BeanNotOfRequiredTypeException
- Spring bean id 重复覆盖的问题解决