Spring源码解析 ---- 循环依赖
2015-04-01 14:10
627 查看
一、循环引用:
1. 定义: 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如CircularityA引用CircularityB,CircularityB引用CircularityC,CircularityC引用CircularityA,形成一个环状引用关系。
2. 代码示例:
CircularityA
CircularityB
CircularityC
3. Spring源码:
在Spring源码的AbstractAutowireCapableBeanFactory类中有如下代码:
这是Spring真正创建Bean的地方, 但是创建Bean就要考虑到处理循环引用又叫做循环依赖的问题。 代码中写到了对单例Bean循环依赖的处理, 大致就是用递归的方法找出当前Bean的所有依赖Bean, 然后全部提前缓存起来。
setter循环依赖(对于setter注入造成的依赖是通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter注入)的Bean来完成的,而且只能解决单例作用域的Bean循环依赖)具体处理步骤如下:
(1) Spring容器创建单例“circularityA” Bean,首先根据无参构造器创建“circularityA” Bean, 并暴露一个exposedObject用于返回提前暴露的Bean,并将“circularityA”Bean放到Catch中;然后进行setter注入“circularityB”;
(2) Spring容器创建单例“circularityB" Bean,首先根据无参构造器创建“circularityB" Bean,并暴露一个exposedObject用于返回提前暴露的Bean,并将“circularityB” Bean放到Catch中,然后进行setter注入“circularityC”;
(3) Spring容器创建单例“circularityC” Bean,首先根据无参构造器创建“circularityC” Bean,并暴露一个exposedObject用于返回暴露的Bean,并将“circularityC” Bean放入Catch中, 然后进行setter注入“circularityA”;进行注入“circularityA”时由于步骤1提前暴露了exposedObject所以从之前的catch里面拿Bean不用重复创建。
(4) 最后在依赖注入“circularityB”和“circularityA”也是从catch里面拿提前暴露的bean, 完成setter注入。
但是对于“prototype”作用域Bean,Spring容器无法完成依赖注入,因为“prototype”作用域的Bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean。
还有一种Spring无法解决的循环依赖方式----构造器循环依赖
如在创建CircularityA类时,构造器需要CircularityB类,那将去创建CircularityB,在创建CircularityB类时又发现需要CircularityC类,则又去创建CircularityC,最终在创建CircularityC时发现又需要CircularityA。 形成环状依赖, 从而被Spring抛出。
Spring容器将每一个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中,因此如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉。
二、 循环调用
1. 定义: 循环调用其实就是一个死循环(这个是无法解决只能提前避免), 最终造成StackOverflow。
2. 工作实例:
在做Hadoop的调度中间件的时候曾经出现过这一个问题, 用到的解决方法其实和Spring的解决循环依赖的思想很相似, Spring用的是缓存, 我们当时用的是一个集合类。
Hadoop工作有一个常见流程: A --> B --> C
A、B、C是必须符合前后顺序的, 但是业务系统的人可能在创建这种顺序时建成A --> B --> C --> A形成一个环状, 那么这就是一种循环调用。 解决思想及时在建立关系时把A、B、C创建的时候就丢入集合类, 如果发现重复那么说明肯定存在某种环在里面, 然后做出相应处理, 就把循环调用提前阻止了。
1. 定义: 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如CircularityA引用CircularityB,CircularityB引用CircularityC,CircularityC引用CircularityA,形成一个环状引用关系。
2. 代码示例:
CircularityA
public class CircularityA { private CircularityB circularityB; public CircularityA() { } public CircularityA(CircularityB circularityB) { this.circularityB = circularityB; } public void setCircularityB(CircularityB circularityB) { this.circularityB = circularityB; } public void a() { circularityB.b(); } }
CircularityB
public class CircularityB { private CircularityC circularityC; public CircularityB() { } public CircularityB(CircularityC circularityC) { this.circularityC = circularityC; } public void setCircularityC(CircularityC circularityC) { this.circularityC = circularityC; } public void b() { circularityC.c(); } }
CircularityC
public class CircularityC { private CircularityA circularityA; public CircularityC() { } public CircularityC(CircularityA circularityA) { this.circularityA = circularityA; } public void setCircularityC(CircularityA circularityA) { this.circularityA = circularityA; } public void c() { circularityA.a(); } }
3. Spring源码:
在Spring源码的AbstractAutowireCapableBeanFactory类中有如下代码:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { // Instantiate the bean. // 忽略此处代码 // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. 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() { public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); } // 以下代码忽略 }
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); if (exposedObject == null) { return exposedObject; } } } } return exposedObject; }
这是Spring真正创建Bean的地方, 但是创建Bean就要考虑到处理循环引用又叫做循环依赖的问题。 代码中写到了对单例Bean循环依赖的处理, 大致就是用递归的方法找出当前Bean的所有依赖Bean, 然后全部提前缓存起来。
setter循环依赖(对于setter注入造成的依赖是通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter注入)的Bean来完成的,而且只能解决单例作用域的Bean循环依赖)具体处理步骤如下:
(1) Spring容器创建单例“circularityA” Bean,首先根据无参构造器创建“circularityA” Bean, 并暴露一个exposedObject用于返回提前暴露的Bean,并将“circularityA”Bean放到Catch中;然后进行setter注入“circularityB”;
(2) Spring容器创建单例“circularityB" Bean,首先根据无参构造器创建“circularityB" Bean,并暴露一个exposedObject用于返回提前暴露的Bean,并将“circularityB” Bean放到Catch中,然后进行setter注入“circularityC”;
(3) Spring容器创建单例“circularityC” Bean,首先根据无参构造器创建“circularityC” Bean,并暴露一个exposedObject用于返回暴露的Bean,并将“circularityC” Bean放入Catch中, 然后进行setter注入“circularityA”;进行注入“circularityA”时由于步骤1提前暴露了exposedObject所以从之前的catch里面拿Bean不用重复创建。
(4) 最后在依赖注入“circularityB”和“circularityA”也是从catch里面拿提前暴露的bean, 完成setter注入。
但是对于“prototype”作用域Bean,Spring容器无法完成依赖注入,因为“prototype”作用域的Bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean。
还有一种Spring无法解决的循环依赖方式----构造器循环依赖
如在创建CircularityA类时,构造器需要CircularityB类,那将去创建CircularityB,在创建CircularityB类时又发现需要CircularityC类,则又去创建CircularityC,最终在创建CircularityC时发现又需要CircularityA。 形成环状依赖, 从而被Spring抛出。
Spring容器将每一个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中,因此如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉。
二、 循环调用
1. 定义: 循环调用其实就是一个死循环(这个是无法解决只能提前避免), 最终造成StackOverflow。
2. 工作实例:
在做Hadoop的调度中间件的时候曾经出现过这一个问题, 用到的解决方法其实和Spring的解决循环依赖的思想很相似, Spring用的是缓存, 我们当时用的是一个集合类。
Hadoop工作有一个常见流程: A --> B --> C
A、B、C是必须符合前后顺序的, 但是业务系统的人可能在创建这种顺序时建成A --> B --> C --> A形成一个环状, 那么这就是一种循环调用。 解决思想及时在建立关系时把A、B、C创建的时候就丢入集合类, 如果发现重复那么说明肯定存在某种环在里面, 然后做出相应处理, 就把循环调用提前阻止了。
相关文章推荐
- Spring源码解析:循环依赖的探测与处理
- Spring源码解析笔记5——循环依赖的解决
- Spring 源码阅读-循环依赖
- Spring源码解析-基于注解依赖注入
- Spring源码解析 依赖注入
- 解析spring循环依赖
- 解析spring循环依赖策略
- spring循环依赖策略解析
- Spring源码解析 依赖注入
- Spring源码(六)-Spring循环依赖的解决方案
- Spring源码解析三:IOC容器的依赖注入
- Spring源码-IOC容器(六)-bean的循环依赖
- spring 源码-循环依赖
- Spring源码学习--Bean对象循环依赖问题解决(四)
- 解析spring循环依赖策略
- spring源码分析 循环依赖
- Spring源码阅读-BeanFactory-循环依赖
- spring 源码探索--单例bean解决循环依赖问题
- Spring源码解析:Bean的实例化与依赖注入(四)
- Spring源码解析 依赖注入