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.

 

posted @   杨岂  阅读(254)  评论(0编辑  收藏  举报
编辑推荐:
· .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技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示