Spring单例循环依赖分析
Spring单例循环依赖分析
前置流程:getBean()之前,BeanDefinition已经被注册到容器中。然后是单例getBean的整体流程,以及出现循环依赖的解决方式。生命周期之类的函数不在本文范围内。文中可能有些东西解释的不准或者不对。
最初的简单疑惑
一开始看源码的一些简单疑惑,提前说明
为什么源码中有很多加锁的地方和线程安全的Map
因为Spring要保证框架特别安全稳定,一般情况,我作为初学者,可能创建或者getBean
的时候不会在多线程下进行,复杂起来就会有这样的场景。所以要保证线程安全。
为什么在getbean的时候,明明没有创建却要把他makeAsCreated()
主要是防止多例对象循环依赖,另外还有线程安全。单例倒不影响,有这个疑惑主要是因为站在单例的角度觉得这个操作没什么太大必要。
为什么操作三个缓存的时候总是put一个的同时remove另两个,为什么需要singletonFactory
三个缓存分别是singletonObjects, earlySingletonObjects,singltonFactory
,第一个存储完全体bean,也就是依赖注入完成的bean,第二个存储尚未完成的bean,第三个存储创建bean的Factory
。
问题一:存第一个remove另外两个好理解,因为已经完成创建,没必要再把之前的引用保留。关键是为什么getSingleton(beanName)
中存入earlySingletonObjects
的时候为什么要remove工厂的缓存。因为工厂中存的是工厂对象,从这里getObject()
相当于创建一个bean。如果get到了就存到早期对象的缓存中,后面就不必再创建了,如果不remove可能还会被调用,再次创建对象。
问题二:singletonFacoty
中可以存我们自己实现的FactoryBean
对象,可以再bean的声明周期中做一些自定义的事情。
为什么原型对象不支持循环依赖
从设计上来讲,原型对象的生命周期不归容器管,所以他说不准什么时候就被gc了,之前依赖他的东西可能就空指针了。
从实现上来讲,getBean
的时候,如果bean没有创建,则会标记正在创建,如果这个原型对象有循环依赖,那么给他依赖注入的时候,他依赖的对象get他的时候,就会发现他正在创建,就会报异常。
// AbstractBeanFactory#doGetBean
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
什么是bean继承
就是继承父类bean的配置信息,和类继承不是一个概念。看源码的时候会看到MergeBeanDefinition
之类的,就是在合并这些信息。
BeanDefinition中的DependsOn数组和依赖到底有什么关系?
DependsOn数组就是手动指定的依赖吗,而不是我们逻辑上认为的那个依赖,例如,这样指定可以确定地让A实例化之前先实例化B。显然这个不能循环依赖。
<bean name="A" class="com.example.springbootleansrccode.MyFactoryBean" lazy-init="true" depends-on="B"/
这就解释了doGetBean
中这段代码的作用,检查有没有这样的循环依赖。若AB循环依赖,首先A走到这里是没问题的,但是他会调这个函数registerDependentBean
注册依赖信息,等到B到这里的时候就会报错。
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
整体函数调用流程
主要给出的是循环依赖的流程,普通的流程比较简单,前者明白后者自然ok。
注,图中给出的是两个循环依赖的类的getbean
流程,其中一个是由FactoryBean
创建对象。
可以自己写代码断点调试更清晰,很多方法分散在各个类中,不容易找到。
贴出第一个getSingleton的代码,先从一二级中取,都没取到,加锁再取,还没取到就去factory中取。
@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) {
// Consistent creation of early reference within full singleton lock
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);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
本文作者:DL
本文链接:https://www.cnblogs.com/BayMax0-0/p/17755043.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步