java 注入 循环_Spring框架是怎么解决Bean之间的循环依赖的 (转)

循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图:

如何理解“依赖”呢,在Spring中有:

构造器循环依赖

field属性注入循环依赖

直接上代码:

构造器循环依赖

@Servicepublic classA {publicA(B b) { }

}

@Servicepublic classB {publicB(C c) {

}

}

@Servicepublic classC {publicC(A a) { }

}

结果:项目启动失败,发现了一个cycle

2.field属性注入循环依赖

@Servicepublic classA1 {

@AutowiredprivateB1 b1;

}

@Servicepublic classB1 {

@AutowiredpublicC1 c1;

}

@Servicepublic classC1 {

@AutowiredpublicA1 a1;

}

结果:项目启动成功

3.field属性注入循环依赖(prototype)

@Service

@Scope("prototype")public classA1 {

@AutowiredprivateB1 b1;

}

@Service

@Scope("prototype")public classB1 {

@AutowiredpublicC1 c1;

}

@Service

@Scope("prototype")public classC1 {

@AutowiredpublicA1 a1;

}

结果:项目启动失败,发现了一个cycle。

现象总结:同样对于循环依赖的场景,构造器注入和prototype类型的属性注入都会初始化Bean失败。因为@Service默认是单例的,所以单例的属性注入是可以成功的。

Spring如何解决循环依赖

spring中循环依赖有三种情况:

1、构造器注入形成的循环依赖。也就是beanB需要在beanA的构造函数中完成初始化,beanA也需要在beanB的构造函数中完成舒适化,这种情况的结果就是两个bean都不能完成初始化,循环依赖难以解决。

2、setter注入构成的循环依赖。beanA需要在beanB的setter方法中完成初始化,beanB也需要在beanA的setter方法中完成初始化,spring设计的机制主要就是解决这种循环依赖,也是今天下文讨论的重点。

3、prototype作用域bean的循环依赖。这种循环依赖同样无法解决,因为spring不会缓存‘prototype’作用域的bean,而spring中循环依赖的解决正是通过缓存来实现的。

下面主要说明第二种情况中循环依赖的解决方案

步骤一:beanA进行初始化,并且将自己进行初始化的状态记录下来,并提前向外暴露一个单例工程方法,从而使其他bean能引用到该bean(可能读完这一句,您仍然心存疑惑,没关系,继续往下读)

步骤二:beanA中有beanB的依赖,于是开始初始化beanB。

步骤三:初始化beanB的过程中又发现beanB依赖了beanA,于是又进行beanA的初始化,这时发现beanA已经在进行初始化了,程序发现了存在的循环依赖,然后通过步骤一中暴露的单例工程方法拿到beanA的引用(注意,此时的beanA只是完成了构造函数的注入但为完成其他步骤),从而beanB拿到beanA的引用,完成注入,完成了初始化,如此beanB的引用也就可以被beanA拿到,从而beanA也就完成了初始化。

spring进行bean的加载的时候,首先进行bean的初始化(调用构造函数),然后进行属性填充。在这两步中间,spring对bean进行了一次状态的记录,也就是说spring会把指向只完成了构造函数初始化的bean的引用通过一个变量记录下来,明白这一点对之后的源码理解至关重要。

源码角度观看循环依赖的解决步骤

步骤一中首先进行beanA的创建

if(mbd.isSingleton()) {

sharedInstance= getSingleton(beanName, () ->{try{returncreateBean(beanName, mbd, args);

}catch(BeansException ex) {//Explicitly remove instance from singleton cache: It might have been put there//eagerly by the creation process, to allow for circular reference resolution.//Also remove any beans that received a temporary reference to the bean.

destroySingleton(beanName);throwex;

}

});

进入getSingleton中,spirng会记录当前beanA正在创建中

if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {throw newBeanCurrentlyInCreationException(beanName);

}

并且将注册一个工厂方法来解决循环依赖

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, ()->getEarlyBeanReference(beanName, mbd, bean));

}

主要就是addSingletonFactory,这句就完成了工厂方法的注册,这个方法可以返回一个只完成了构造函数初始化的beanA,也许大家想知道他是如何返回的,我们进入getEarlyBeanReference方法

protectedObject getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {

Object exposedObject=bean;if (!mbd.isSynthetic() &&hasInstantiationAwareBeanPostProcessors()) {for(BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceofSmartInstantiationAwareBeanPostProcessor) {

SmartInstantiationAwareBeanPostProcessor ibp=(SmartInstantiationAwareBeanPostProcessor) bp;

exposedObject=ibp.getEarlyBeanReference(exposedObject, beanName);

}

}

}returnexposedObject;

}

可以看到这个方法中,除了对后处理器的调用,没有进行任何动作,而是直接返回了我们参数传入的bean,那么这个bean是哪来的呢?其实在这之前,spring先调用了beanA的构造函数,并拿到了只完成了构造函数初始化的一个实例,并把他记录了下来。

if(mbd.isSingleton()) {

instanceWrapper= this.factoryBeanInstanceCache.remove(beanName);

}if (instanceWrapper == null) {

instanceWrapper=createBeanInstance(beanName, mbd, args);

}final Object bean = instanceWrapper.getWrappedInstance();

这就是这个bean的由来。然后当我们进行到步骤三的时候,就会检查是否允许循环依赖(即使是Singleton类型的bean也可以通过参数设置,禁止循环依赖),如果允许的话,就会通过这个工厂方法拿到beanA的引用。从而完成beanA和beanB的加载。

protected Object getSingleton(String beanName, booleanallowEarlyReference) {

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

}

}

}

}returnsingletonObject;

}

出处:

https://cloud.tencent.com/developer/article/1455953

https://www.jianshu.com/p/8bb67ca11831

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值