spring的依赖循环--记录一次工作时的偶遇

spring的依赖循环--记录一次工作时的偶遇,第1张

spring的依赖循环--记录一次工作时的偶遇

目录

一、问题

二、代码查看

三、如何解决循环依赖?


注:如果文章出现错误,请大佬们指正,共同学习下,谢谢 一、问题

一天同事和我说我们的项目发生了循环依赖,把我给震惊到了,直呼:666

然后默默的翻日志查看是什么的问题

 

关于循环依赖,相信大家都是非常熟悉的,A依赖B,B又依赖A,它们之间就会形成了循环依赖。或是A依赖B,B依赖C,C又依赖A,它们的依赖关系如同下图:

 

 

二、代码查看

通过查看原本项目的代码,发现了这样一段代码

BeanA.java代码

@Component
public class BeanA {

    @Autowired
    BeanB beanB;

}

BeanB.java

@Component
public class BeanB {

    private  BeanA beanA;

    @Autowired
    public BeanB(BeanA beanA) {
        this.beanA = beanA;
    }
}

好家伙,构造器注入

解决方案:

@Component
public class BeanB {

    @Autowired
    private  BeanA beanA;

  
}

如果熟悉IOC的加载流程,相信你已经知道是怎么回事了。

 在Bean调用构造器实例化之前,一二三级缓存并没有Bean的任何信息,在实例化之后才放入三级缓存中,因此当getBean的时候缓存并没有命中,这样就抛出了循环依赖的异常了。

三、如何解决循环依赖?

3.1 三级缓存

 3.2 创建原始Bean对象

// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		Object bean = instanceWrapper.getWrappedInstance();

假如是beanA先创建,创建后的对象是BeanA@12345,上面的代码BeanB的beanA变量指向就是改对象。

3.3暴露早期引用

该方法用于把早期对象包装成一个ObjectFactory暴露到三级缓存中,用于解决循环依赖

protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			//如果一级缓存不存在该bean
			if (!this.singletonObjects.containsKey(beanName)) {
				//加入到三级缓存中,,,,,暴露早期对象用于解决循环依赖
				this.singletonFactories.put(beanName, singletonFactory);
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}

3.4解析依赖

//populateBean 用于向 beanA 这个原始对象中填充属性,当它检测到 beanA 依赖于 beanB 时,
//会首先去实例化 beanB。
//beanB 在此方法处也会解析自己的依赖,当它检测到 beanA 这个依赖,于是调用 
//BeanFactroy.getBean("beanA") 这个方法,从容器中获取 beanA。

populateBean(beanName, mbd, instanceWrapper);

3.5 获取早期的引用

@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		
		Object singletonObject = this.singletonObjects.get(beanName);
		
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			
			singletonObject = this.earlySingletonObjects.get(beanName);
			
			if (singletonObject == null && allowEarlyReference) {
				//加锁
				synchronized (this.singletonObjects) {
					// 重新从一级缓存取
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						//重新从二级缓存取
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
							
							ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
							//从三级缓存中获取到对象不为空
							if (singletonFactory != null) {
								
								singletonObject = singletonFactory.getObject();
								//把早期对象放置在二级缓存,
								this.earlySingletonObjects.put(beanName, singletonObject);
								//ObjectFactory 包装对象从三级缓存中删除掉
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

接着上面的步骤讲:

1.populateBean 调用 BeanFactroy.getBean("beanA") 以获取 beanB 的依赖。

2.getBean("beanB") 会先调用 getSingleton("beanA"),尝试从缓存中获取 beanA。此时由于 beanA 还没完全实例化好

3.于是 this.singletonObjects.get("beanA") 返回 null。

4.接着 this.earlySingletonObjects.get("beanA") 也返回空,因为 beanA 早期引用还没放入到这个缓存中。

5.最后调用 singletonFactory.getObject() 返回 singletonObject,此时 singletonObject != null。singletonObject 指向 BeanA@1234,也就是 createBeanInstance 创建的原始对象。此时 beanB 获取到了这个原始对象的引用,beanB 就能顺利完成实例化。beanB 完成实例化后,beanA 就能获取到 beanB 所指向的实例,beanA 随之也完成了实例化工作。由于 beanB.beanA 和 beanA 指向的是同一个对象 BeanA@1234,所以 beanB 中的 beanA 此时也处于可用状态了。

如上面的流程来说:

1.为什么需要二级缓存?

一级缓存和二级缓存相比:二级缓存只要是为了分离成熟Bean和纯净Bean(未注入属性)的存放, 防止多线程中在Bean还未创建完成时读取到的Bean时不完整的。所以也是为了保证我们getBean是完整最终的Bean,不会出现不完整的情况。一二三级缓存下二级缓存的意义:二级缓存为了存储 三级缓存的创建出来的早期Bean, 为了避免三级缓存重复执行。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5707006.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存