Spring源代码解析 ---- 循环依赖

转载 2018年04月17日 20:15:38

一、循环引用


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 源码探索--单例bean解决循环依赖问题

spring 中循环依赖问题: ItemA 依赖ItemB,ItemB依赖ItemC,ItemC依赖ItemA,这就造成了循环依赖。 循环依赖有两种实现方式:构造函数,setter注入 单例...
  • crazyzhb2012
  • crazyzhb2012
  • 2016-11-26 17:45:32
  • 1183

Spring源码解析 ---- 循环依赖

做Hadoop的调度中间件的时候曾经出现过这一个问题, 用到的解决方法其实和Spring的解决循环依赖的思想很相似, Spring用的是.........
  • wenniuwuren
  • wenniuwuren
  • 2015-04-01 14:10:24
  • 4207

Spring循环依赖的三种方式

引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错。下面说一下Spring是如果解决循环依赖的。 第一...
  • u010644448
  • u010644448
  • 2017-03-01 15:46:16
  • 6286

spring处理循环依赖

看spring源代码时,发一段代码如下,不明白什么意思。百度一下,才有点弄明白,记录一下,。 protected Object getSingleton(String beanName, boole...
  • u013485533
  • u013485533
  • 2015-03-16 17:04:32
  • 1876

spring循环依赖问题排查

一、背景 清分服务添加一个异步处理功能(@asyc),本地测试时发现启动服务后有时正常有时异常。   二、代码再现 1、启动类代码 p...
  • lianhuazy167
  • lianhuazy167
  • 2017-03-27 08:16:46
  • 652

Spring - Bean 循环依赖问题

一、准备工作 下面新建一个Maven工程的Web项目,其中有两个实体类分别如下:package com.egov.pojo;/** * Created by wuguoping on 2017/9...
  • RHCDS
  • RHCDS
  • 2017-11-11 09:46:00
  • 198

Spring Bean 循环依赖解决方案

由于service层互相调用,一下子没有什么好的拆分方案,所以先解决循环依赖问题。现有AService 和BService,都在对方bean中注入,导致初始化时循环初始报错,解决方案就是选择其一使其延...
  • u011403655
  • u011403655
  • 2016-07-22 13:55:50
  • 5151

spring 源代码 循环依赖

在使用spring的场景中,有时会碰到如下的一种情况,即bean之间的循环引用。即两个bean之间互相进行引用的情况。这时,在spring xml配置文件中,就会出现如下的配置: ...
  • gxftry1st
  • gxftry1st
  • 2017-03-10 16:15:09
  • 415

Spring中的循环依赖问题介绍及解决方法

Spring容器能顺利的实例化以构造函数注入方式配置的bean有一个前提:即bean构造函数入参引用的对象必须已经准备就绪。 那么如果两个bean都采用构造函数注入,并且都通过构造函数入参引用了对方...
  • icarus_wang
  • icarus_wang
  • 2016-06-05 10:32:14
  • 3312

Spring-bean的循环依赖以及解决方式

本文主要是分析Spring bean的循环依赖,以及Spring的解决方式。 通过这种解决方式,我们可以应用在我们实际开发项目中。 什么是循环依赖? 怎么检测循环依赖 Spri...
  • u010853261
  • u010853261
  • 2017-09-12 08:18:21
  • 3430
收藏助手
不良信息举报
您举报文章:Spring源代码解析 ---- 循环依赖
举报原因:
原因补充:

(最多只允许输入30个字)