1、循环引用:java
1. 定义: 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比方CircularityA引用CircularityB,CircularityB引用CircularityC,CircularityC引用CircularityA。造成一个环状引用关系。缓存
2. 代码演示样例:oop
CircularityA
this
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(); } }
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(); } }
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(); } }
在Spring源代码的AbstractAutowireCapableBeanFactory类中有例如如下代码:spa
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; }
代码中写到了对单例Bean循环依赖的处理。 大体就是用递归的方法找出当前Bean的全部依赖Bean, 而后全部提早缓存起来。prototype
setter循环依赖(对于setter注入形成的依赖是经过Spring容器提早暴露刚完毕构造器注入但未完毕其它步骤(如setter注入)的Bean来完毕的,而且仅仅能解决单例做用域的Bean循环依赖)详细处理过程例如如下:debug
(1) Spring容器建立单例“circularityA” Bean。首先依据无參构造器建立“circularityA” Bean, 并暴露一个exposedObject用于返回提早暴露的Bean。并将“circularityA”Bean放到Catch中。而后进行setter注入“circularityB”;code
(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池”中清除掉。
2、 循环调用
1. 定义: 循环调用事实上就是一个死循环(这个是没法解决仅仅能提早避免), 终于形成StackOverflow。
2. 工做实例:
在作Hadoop的调度中间件的时候之前出现过这一个问题, 用到的解决方法事实上和Spring的解决循环依赖的思想很是类似。 Spring用的是缓存, 咱们当时用的是一个集合类。
Hadoop工做有一个常见流程: A --> B --> C
A、B、C是必须符合先后顺序的。 但是业务系统的人可能在建立这样的顺序时建成A --> B --> C --> A造成一个环状。 那么这就是一种循环调用。
解决思想及时在创建关系时把A、B、C建立的时候就丢入集合类。 假设发现反复那么说明确定存在某种环在里面。 而后作出对应处理。 就把循环调用提早阻止了。