Spring解决循环依赖
Spring解决循环依赖
什么是循环依赖:比如A引用B,B引用C,C引用A,它们最终形成一个依赖环。
循环依赖有两种
1、构造器循环依赖
构造器注入导致的循环依赖,Spring是无法解决的,只能抛出BeanCurrentlyInCreationException异常。因为构造器注入时的参数需要依赖bean的实例。所以无法解决循环依赖的问题。
如在创建A的实例时,发现依赖B,那将去创建B的实例,又发现依赖C,则又去创建C,最终在创建C的时候发现依赖A,从而形成一个环,没办法创建。
Spring容器每一个正在创建的bean标识符放在一个“当前创建bean池”中,这个池是一个set集合
private final Set<String> singletonsCurrentlyInCreation = Collections.synchronizedSet(new HashSet());
bean标识符将一直保持在这个池中,因此如果bean创建过程中发现自己已经在当前创建bean池中时,将抛出BeanCurrentlyInCreationException异常表示循环依赖。而对于创建完毕的bean将从“当前创建bean”池中清除掉。
2、setter循环依赖
表示通过setter注入方式构成的循环依赖。对于setter注入造成的依赖是通过Spring容器提前暴露刚完成构造器创建实例但未完成其他步骤如setter注入的bean来完成的。提前暴露一个单例工厂方法,从而使其他bean能引用该bean。
而且只能解决单例作用域的bean循环依赖。
步骤:
1)Spring容器创建单例A bean,首先通过无参构造器创建bean,并暴露一个ObjectFactory返回一个在创建中的bean,并将A标识符放在当前创建bean池中。然后通过setter方式注入依赖B
2)Spring容器创建单例B bean,首先通过无参构造器创建bean,并暴露一个ObjectFactory返回一个在创建中的bean,并将B标识符放在当前创建bean池中。然后通过setter方式注入依赖C
3)Spring容器创建单例C bean,首先通过无参构造器创建bean,并暴露一个ObjectFactory返回一个正在创建中的bean,并将C标识符放在当前创建bean池中。然后setter方式注入A,进行注入A时由于提前暴露了ObjectFactory工厂,所以使用它返回提前暴露一个创建中的bean。
4)然后再依次完成setter注入C完成B创建,依赖注入B完成A创建。
prototype范围的依赖处理:spring也无法完成依赖注入,因为Spring容器不进行缓存prototype作用域的bean,因此无法暴露一个创建中的bean。(单例范围下是通过放入缓存中的ObjectFactory来创建实例)
缓存objectFactory是将ObjectFactory放入map中
private final Map<String, ObjectFactory> singletonFactories = new HashMap(); this.singletonFactories.put(beanName, singletonFactory); this.addSingletonFactory(beanName, new ObjectFactory() { public Object getObject() throws BeansException { // 在此完成aop动态织入 return AbstractAutowireCapableBeanFactory.this.getEarlyBeanReference(beanName, mbd, bean); } });
aop将advice动态织入bean中也是在将创建bean的工厂放在单例工厂集合中时织入的。
常规bean的创建是在AbstratAutowireCapableBeanFactory的doCreateBean方法中创建的。
创建完成的bean,如果配置了destory-method则放在LinkHashMap中,便于在销毁时调用。
private final Map<String, Object> disposableBeans = new LinkedHashMap();
Spring解决循环依赖有两个条件:
1、不全是构造器方式的循环依赖
2、必须是单例
解决方式是通过三级缓存。
一级缓存:singletonObjects,用来保存实例化、初始化都完成的bean对象
二级缓存:earlySingletonObjects,用来保存实例化完成,但是未初始化完成的对象
三级缓存:singletonFactories,用来保存一个ObjectFactory,表示当前bean正在实例化,提供一个匿名内部类,用于创建二级缓存中的对象
使用三级缓存来保证不管什么时候使用的都是一个对象。
END.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类