https://zhuanlan.zhihu.com/p/405669772
1. 什么是循环依赖
循环依赖其实就是循环引用,也就是两个或者两个以上的Bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C依赖于A。
注意,这里不是函数的循环调用,是对象的相互依赖关系,循环调用其实就是一个死循环,除非有终结条件。
Spring中循环依赖场景有:
·构造器的循环依赖(构造器注入)
·Field属性的循环依赖(set注入)
其中,构造器的循环依赖问题无法解决,只能抛出BeanCurrentlyInCreationException异常,在解决属性循环依赖时,spring采用的是提前暴露对象的方法。
2.循环依赖处理机制
·单例bean构造器参数循环依赖(无法解决)
·prototype原型bean循环依赖(无法解决)
对于原型bean的初始化过程中无论是通过构造器参数循环依赖还是通过setXXX方法产生循环依赖,spring都会直接报错处理。
AbstractBeanFactory.doGetBean()方法
1 2 3 | if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }<br><br> |
1 2 3 4 5 | <br> protected boolean isPrototypeCurrentlyInCreation(String beanName) { Object curVal = this .prototypesCurrentlyInCreation.get(); return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); } |
在获取bean之前如果这个原型bean正在被创建则直接抛出异常。原型bean在创建之前会进行标记这个beanName正在被创建,等创建结束之后会删除标记。
1 2 3 4 5 6 7 8 9 | try { //创建原型bean之前添加标记 beforePrototypeCreation(beanName); //创建原型bean<br> prototypeInstance = createBean(beanName, mbd, args); } finally { //创建原型bean之后删除标记<br> afterPrototypeCreation(beanName); } |
总结:Spring不支持原型bean的循环依赖。
·单例bean通过setXXX或者@Autowired进行循环依赖
Spring的循环依赖的理论依据基于java的引用传递,当获得对象的引用时,对象的属性是可以延后设置的,但是构造器必须是在获取引用之前。
Spring通过setXXX或者@Autowired方法解决依赖其实是通过提前暴露一个ObjectFactory对象来完成的,简单来说classA在调用构造器完成对象初始化之后,在调用classA的setClassB方法之前就把classA实例化的对象通过ObjectFactory提前暴露到spring容器。
·Spring容器初始化classA通过构造器初始化对象后提前暴露到spring容器。
1 2 3 4 5 6 7 8 9 | 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" ); }<br> //将初始化后的对象提前已ObjectFactory对象注入到容器中 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } |
·ClassA调⽤setClassB⽅法,Spring⾸先尝试从容器中获取ClassB,此时ClassB不存在Spring 容器中。
·Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中
·ClassB调⽤setClassA⽅法,Spring从容器中获取ClassA ,因为第⼀步中已经提前暴露了 ClassA,因此可以获取到ClassA实例
ClassA通过spring容器获取到ClassB,完成了对象初始化操作。
·这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题。
3. 源码分析
首先了解在DefaultSingletonBeanRegistry类中对缓存的定义。
/** 一级缓存:单例对象池,存放完整的SpringBean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 三级缓存:单例工厂对象 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** 二级缓存:早期单例对象 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
AbstractBeanFactory类的doGetBean方法中有如下代码来创建bean实例。getSingleton这个方法就是从一级缓存中获取bean,如果为null,则创建bean从三级缓存获取对象,
主要看下createBean方法。
场景:A依赖B,B依赖A
通过springIOC源码剖析(https://blog.csdn.net/jeoly33/article/details/118424610),我们可以直接把代码定位到AbstractApplicationContext的refresh方法-finishBeanFactoryInitialization(实例化bean实例)-
beanFactory.preInstantiateSingletons(在此方法中遍历初始化的所有beanName)-AbstractBeanFactory.getBean-doGetBean
首先创建A,会在doGetBean方法中会先从一级缓存去拿发现没有,且对象没有在创建中,返回null。
1 2 3 4 5 6 7 8 9 10 11 12 | Object sharedInstance = getSingleton(beanName);<br> //已经存在则返回 if (sharedInstance != null && args == null ) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) {<br> logger.trace( "Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference" ); } else { logger.trace( "Returning cached instance of singleton bean '" + beanName + "'" ); } } bean = getObjectForBeanInstance(sharedInstance, name, beanName, null ); } |
@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 先从一级缓存取
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);
//从三级缓存移除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
此时是空的,则需要去创建对象,往下看有如下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //创建单例bean<br>if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(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); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } |
进入createBean方法,继续走到AbstractAutowireCapableBeanFactory类的doCreateBean方法
1 2 3 4 5 6 7 8 9 10 11 12 | protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null ; if (mbd.isSingleton()) { instanceWrapper = this .factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null ) {<br> //创建bean实例,仅仅调用构造方法,但是尚未设置属性 instanceWrapper = createBeanInstance(beanName, mbd, args); } ... |
这个类往下看有如下方法如果单例、允许循环依赖、bean在创建过程中则加入三级缓存,存放的是lambda表达式,会去创建代理对象。
1 2 3 4 5 6 7 8 9 | 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" ); }<br> //加入三级缓存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } |
并且从二级缓存移除。
1 2 3 4 5 6 7 8 9 10 | 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); } } } |
接着是属性的填充,在这个方法中完成依赖对象的创建。
1 2 3 4 5 6 | Object exposedObject = bean; try { //属性填充 populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); } |
1 | //属性填充applyPropertyValues(beanName, mbd, bw, pvs); |
重点看下populateBean的applyPropertyValues(beanName, mbd, bw, pvs)完成属性填充。即对依赖B的创建
1 2 3 | //得到需要注入的名字<br>String propertyName = pv.getName(); Object originalValue = pv.getValue();<br> //查找bean Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); |
1 2 3 4 5 6 7 8 | //进入解决引用方法<br>public Object resolveValueIfNecessary(Object argName, @Nullable Object value) { // We must check each value to see whether it requires a runtime reference // to another bean to be resolved. if (value instanceof RuntimeBeanReference) { RuntimeBeanReference ref = (RuntimeBeanReference) value; //解决引用 return resolveReference(argName, ref); } |
此时refName为B,会去创建B。
1 2 3 4 | else {<br> //从bean工厂获取引用bean bean = this .beanFactory.getBean(refName); this .beanFactory.registerDependentBean(refName, this .beanName); } |
接着会走到方法doGetBean,跟前面创建A的流程一样。尝试去缓存中获取依赖的bean对象,此时也是null,会去创建bean对象并实例化,将实例化好的对象放入三级缓存中,接着会进行属性注入实现方法填充,这时会找到依赖A,进入解决值方法那个方法中,从bean工厂获取bean。
再次走到getSingleton方法去获取A,先从一级缓存获取,此时对象在创建中,接着从二级缓存获取,为null且运行引用,继续判断,从三级缓存获取,之前A已经放入到三级缓存了,发现不为null,通过工厂创建依赖所需要的对象,将对象放入二级缓存暴露自己,从三级缓存中移除,返回代理对象,完成属性的注入。然后一直返回到getSingleton中有如下方法,此时B已经注入A,B走完完整 创建周期,会添加到一级缓存。
1 2 3 | if (newSingleton) {<br> //添加一级缓存 从三级缓存和二级缓存中移除 addSingleton(beanName, singletonObject); } |
接着返回到doCreateBean的下面方法。此时A没有在一级缓存,二级缓存中拿到直接返回,重新赋值没有代理的对象。接着在添加到一级缓存,从二级和三级缓存中删除。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false ); if (earlySingletonReference != null ) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (! this .allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example." ); } } } } |
为什么需要二级缓存?
如果将三级缓存的代理对象直接放入一级缓存,则一级缓存中存在两种状态的bean,一个已经走完spring完整周期的bean
,一个是刚生成完代理对象的bean,违反了单一原则。
二级缓存还有个重要作用,起到公用的作用,如果一个bean依赖两个bean,同时这两个bean依赖又都依赖于这个bean,当在二级缓存放入对象后,另一个bean可以直接从二级缓存去获取bean。
存放涉及到循环依赖的对象。
为什么需要三级缓存?
产生代理对象。如果注入的bean不是代理对象,则需要一二级缓存就可以了,不需要三级缓存,三级缓存通过后置处理器产生代理对象完成注入。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗