Spring IOC(七)Spring 中循环依赖的解决

首先抛出结论,Spring 目前无法解决 构造方法循环依赖,而可以解决字段的循环依赖。
例如以下两种情况:

  1. 字段依赖
    CyclicA:
@Component
public class CyclicA {
    @Autowired
    private CyclicB cyclicB;
}

CyclicB

@Component
public class CyclicB {
    @Autowired
    private CyclicA cyclicA;
}
  1. 构造方法依赖
    CyclicC
@Component
public class CyclicC {
    private CyclicD cyclicD;
    @Autowired
    public CyclicC( CyclicD cyclicD) {
        this.cyclicD = cyclicD;
    }
}

CyclicD

public class CyclicC {
    private CyclicD cyclicD;
    @Autowired
    public CyclicC( CyclicD cyclicD) {
        this.cyclicD = cyclicD;
    }
}

对于上面两个例子,1可以成功注入,而2则无法进行注入。
下面以上面两种情况来具体分析Spring 源代码。

字段注入

从前面几篇文章中,了解到,SpringgetBean方法 主体流程为 实例化 Bean和 初始化 Bean 实例(包括初始化字段和 调用init方法)。
而 实例化Bean则是调用其构造方法进行调用,而初始化则是对 @Autowired@Value bean 进行赋值等操作,其原来也是对依赖进行进一步 getBean 调用。所以并不会报错。
从前面文章 分析 在getBean时候,在getSingleton时:

	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;
	}
  1. 首先会尝试从 singletonObjects 中获取
  2. 而后如果beanName正在创建中 isSingletonCurrentlyInCreation ,则会从 earlySingletonObjects 中 获取。
  3. 如果 earlySingletonObjects 再一次为null,而 允许获取,则会从 singletonFactories 中获取构造工厂,如果获取到了则利用其去获取构造方法。而从前面分析, singletonFactories 是的是一个lambda 表达式。

另一方面,在每一个bean实例化时候,都会进入 doCreateBean,并且在 实例化后(populateBean前)将类信息放入缓存中:

		// 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.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

addSingletonFactory 则是将信息放入到 singletonFactoriesregisteredSingletons 中。

	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);
			}
		}
	}

所以当A依赖B,而B又依赖A是:

  1. A首先完成实例化第一步,并且将自己提前曝光在 singletonFactoriesregisteredSingletons 中。
  2. 而后,A开始执行 popularBean方法,在 对 @Autowired 字段进行解析时,会getBean获取B,此时如果B没有创建,就会去创建 B。
  3. 创建B的过程中,由于B 中有对 A@Autowired,此时就会去getBean获取A。
  4. 此时尝试 singletonObjects没有,而尝试 earlySingletonObjects 中获取没有,最后在 singletonFactories 中获取到了。所以此时B拿到了A,B完成了初始化。
  5. 而后A从 singletonObjects 中获取到了B, 所以最终A结束了getBean获取B的操作。
  6. 至此,A拿到了B,B拿到了A,都完成了初始化。

构造方法注入

在getBean时候,从getBean->doGetBean->getSingleton->beforeSingletonCreation,在 beforeSingletonCreation 会有一次检查,检查 当前bean是否在创建中,如果 在创建bean时,发现自己已经在当前创建的bean池中,那么就会报错:
在这里插入图片描述
构造器注入和字段注入不一样,beforeSingletonCreation 执行比 实例化字段早,即在实例化之前,先检查自己是否在创建中:

	protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

最终singletonsCurrentlyInCreation 会同时有 CyclicCCyclicD

非单例循环依赖

对于“prototype”作用域bean, Spring 容器无法完成依赖注入,因为Spring 容器不进行缓 存“prototype”作用域的bean ,因此无法提前暴露一个创建中的bean 。
但是可以使用 @LookUp在单例中使用非单例(prototype) 实例。

觉得博主写的有用,不妨关注博主公众号: 六点A君。
哈哈哈,一起研究Spring:
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值