美文网首页
Spring循环依赖

Spring循环依赖

作者: 帅气滴糟老头 | 来源:发表于2024-01-09 16:36 被阅读0次

    编码解决

    通过良好的编码习惯可以避免Spring循环依赖
    方式主要有三种:
    1. 构造函数注入: 这是最佳实践,通过构造函数注入可以避免循环依赖。在一个类的构造函数中注入其依赖,而不是通过字段注入。

    @Service
    public class A {
        private final B b;
    
        @Autowired
        public A(B b) {
            this.b = b;
        }
    }
    
    @Service
    public class B {
        private final A a;
    
        @Autowired
        public B(A a) {
            this.a = a;
        }
    }
    

    2. Setter方法注入: 使用Setter方法注入可以解决一部分循环依赖的问题,但不如构造函数注入优雅。

    @Service
    public class A {
        private B b;
    
        @Autowired
        public void setB(B b) {
            this.b = b;
        }
    }
    
    @Service
    public class B {
        private A a;
    
        @Autowired
        public void setA(A a) {
            this.a = a;
        }
    }
    

    3. @Lazy注解: 通过在其中一个依赖上使用@Lazy注解,可以延迟初始化被注入的Bean,从而解决循环依赖。

    @Service
    public class A {
        @Autowired
        @Lazy
        private B b;
    }
    
    @Service
    public class B {
        @Autowired
        @Lazy
        private A a;
    }
    

    为什么构造函数注入可以解决循环依赖?

    Spring 容器创建一个 Bean 的过程包括以下步骤:

    1. 实例化 Bean 对象: Spring 容器根据配置或注解等方式,实例化一个 Bean 对象。这一步是通过调用 Bean 对应类的构造函数完成的。

    构造函数注入能够解决循环依赖的原因主要是因为在 Java 对象创建的过程中,构造函数是最先被调用的,而在构造函数中完成了对象的初始化工作。通过构造函数注入,Spring 在创建对象时可以确保依赖的对象已经初始化完毕,避免了循环依赖的问题。

    1. 依赖注入: 容器对 Bean 进行属性注入。这可以通过构造函数注入、Setter 方法注入或字段注入等方式完成。这是实现控制反转(IoC)的关键步骤,将依赖关系从应用程序代码中解耦,由容器负责管理。

    因为通过构造函数注入,在依赖注入环节需要注入的Bean在上一个实例化阶段就初始化完毕了,就不存在循环依赖问题了

    1. BeanPostProcessor 处理: 如果在容器中注册了 BeanPostProcessor,Spring 会在实例化 Bean 以及依赖注入之后调用它们的回调方法。这提供了一种机制,使开发者可以在 Bean 实例创建的不同生命周期阶段进行定制操作。

    2. 初始化方法调用: 如果 Bean 实现了 InitializingBean 接口,或者在配置文件中通过 <init-method> 或 @PostConstruct 注解指定了初始化方法,Spring 会在依赖注入之后调用初始化方法。这个阶段是为了让开发者执行一些初始化逻辑。

    3. BeanPostProcessor 处理(后置初始化): 如果注册了 BeanPostProcessor,Spring 会在初始化方法调用后再次调用它们的回调方法。这一步称为后置初始化,提供了对 Bean 进行最后处理的机会。

    4. 将 Bean 放入容器: 容器将创建完成并初始化的 Bean 放入自己的 Bean 容器中,以便供其他 Bean 或组件进行依赖注入。


    那三级缓存解决循环依赖是怎么回事?

    Spring 中通过三级缓存(三级Map)的方式来解决循环依赖问题。这三级缓存的主要作用是在处理 Bean 的创建过程中,记录 Bean 的创建状态,避免循环依赖导致的死循环。

    三级缓存包括:

    1. singletonObjects: 保存完全初始化并准备就绪的单例 Bean。在这个 Map 中,Bean 的名字作为键,Bean 实例作为值。在正常情况下,一个单例 Bean 只会放入该缓存一次。

    2. earlySingletonObjects: 保存已经实例化但未完成初始化的 Bean。这是为了解决循环依赖的问题。在这个阶段,Bean 实例被提前暴露给其他 Bean,但尚未执行完整的初始化。同样,Bean 的名字作为键,Bean 实例作为值。

    3. singletonFactories: 保存 Bean 工厂的提供者,即创建 Bean 的工厂对象。同样,Bean 的名字作为键,Bean 工厂实例作为值。这个阶段主要用于处理循环依赖。

    下面是 Spring 处理循环依赖的基本流程:

    1. 尝试获取 Bean:

    首先,从 singletonObjects 中尝试获取 Bean。如果找到,直接返回。
    如果 singletonObjects 中没有找到,再尝试从 earlySingletonObjects 中获取 Bean。如果找到,直接返回。这是为了解决循环依赖的问题,即使 Bean 尚未完全初始化,也可以提前暴露给其他 Bean 使用。

    1. 创建 Bean:

    如果在 singletonObjects 和 earlySingletonObjects 中都没有找到 Bean,则从 singletonFactories 中获取 ObjectFactory,然后使用工厂创建 Bean 的原始实例。
    在创建过程中,将正在创建的 Bean 放入 earlySingletonObjects。

    1. 初始化 Bean:

    完成 Bean 的创建和初始化后,将 Bean 从 earlySingletonObjects 移动到 singletonObjects 中,表示 Bean 创建完成。
    所以,earlySingletonObjects 中的 Bean 是在 Bean 创建的过程中,尚未完成初始化的实例,目的是为了提前处理循环依赖的情况。

    总结起来,earlySingletonObjects 在 Bean 创建的过程中使用,用于解决循环依赖。在整个生命周期中,Bean 的状态会在这三个缓存之间转移。

    相关文章

      网友评论

          本文标题:Spring循环依赖

          本文链接:https://www.haomeiwen.com/subject/kvkandtx.html