当前位置:   article > 正文

【面试宝典】深入了解Spring的循环依赖解决策略_循环依赖问题在spring中主要有三种情况

循环依赖问题在spring中主要有三种情况


579a429daf314744b995f37351b46548

强烈推荐

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站:人工智能

b004071ozy_05_amzn


什么是spring循环依赖问题?

Spring框架中,循环依赖问题指的是在依赖注入时,由于Bean之间相互引用而导致的初始化问题。

这种情况下,Spring容器在创建Bean的过程中,发现Bean A依赖于Bean B,而Bean B又依赖于Bean A,形成了循环依赖关系。


循环依赖的三种情况:

1.构造器循环依赖:

当两个或多个Bean的构造函数相互依赖时,会形成构造器循环依赖。这种情况下,Spring容器在创建Bean时无法确定哪个Bean应该先被实例化,因为它们相互依赖于彼此的构造函数参数。

示例:

public class A {
    private B b;

    public A(B b) {
        this.b = b;
    }
}

public class B {
    private A a;

    public B(A a) {
        this.a = a;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
2.属性循环依赖:

当两个或多个Bean的属性相互依赖时,会形成属性循环依赖。例如,Bean A依赖于Bean B的属性,而Bean B又依赖于Bean A的属性,形成了属性循环依赖。

示例:

public class A {
    private B b;

    public void setB(B b) {
        this.b = b;
    }
}

public class B {
    private A a;

    public void setA(A a) {
        this.a = a;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
3.单例Bean的循环依赖:

当单例Bean之间相互依赖时,会形成单例Bean的循环依赖。由于Spring默认情况下会将单例Bean存储在容器中,这种循环依赖问题可能会导致死锁或无限递归调用。

示例:

假设我们有两个类 AB,它们相互依赖:

public class A {
    private B b;

    public A() {
        // 无参构造函数
    }

    public void setB(B b) {
        this.b = b;
    }
}

public class B {
    private A a;

    public B() {
        // 无参构造函数
    }

    public void setA(A a) {
        this.a = 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

假设 A 类的一个实例需要依赖 B 类的一个实例,而 B 类的一个实例又需要依赖 A 类的一个实例,形成了循环依赖。在这种情况下,如果我们试图使用 Spring 容器来管理这些类的实例,就会出现循环依赖的问题。

例如,我们在 Spring 的配置文件中定义了这两个类的 Bean:

<bean id="a" class="com.example.A">
    <property name="b" ref="b"/>
</bean>

<bean id="b" class="com.example.B">
    <property name="a" ref="a"/>
</bean>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在初始化这些 Bean 时,Spring 会发现 A 类的实例需要 B 类的实例,而 B 类的实例又需要 A 类的实例,这样就形成了循环依赖。如果不采取措施来解决这个问题,Spring 容器就会陷入死循环或者抛出异常。


如何解决?

这三种循环依赖问题在Spring中都有解决方案:

1.构造器循环依赖解决方案:

可以通过使用@Lazy注解延迟初始化其中一个依赖,或者使用@Autowired@Qualifier来指定构造器参数的具体Bean。

示例代码:

@Component
public class A {
    private B b;

    @Autowired
    public A(@Lazy B b) {
        this.b = b;
    }
}

@Component
public class B {
    private A a;

    @Autowired
    public B(A a) {
        this.a = a;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
2. 属性循环依赖解决方案:

可以通过@Lazy注解延迟初始化其中一个依赖,或者将其中一个依赖设置为@Nullable,允许属性为空。

示例代码:

@Component
public class A {
    private B b;

    @Autowired
    public void setB(@Lazy B b) {
        this.b = b;
    }
}

@Component
public class B {
    private A a;

    @Autowired
    public void setA(A a) {
        this.a = a;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
3. 单例Bean的循环依赖解决方案:

可以通过使用@Lazy注解延迟初始化其中一个依赖,或者使用代理对象来解决单例Bean的循环依赖。

示例代码:

<bean id="a" class="com.example.A" lazy-init="true">
    <property name="b" ref="b"/>
</bean>

<bean id="b" class="com.example.B" lazy-init="true">
    <property name="a">
        <bean class="org.springframework.beans.factory.config.BeanReferenceFactoryBean">
            <property name="beanName" value="a"/>
        </bean>
    </property>
</bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这些解决方案可以根据具体情况选择适合的方式来解决Spring中的循环依赖问题。


什么是三级缓存?

在Spring框架中,三级缓存是用来解决循环依赖问题的一种机制。它由Spring容器中的三个缓存组成,用于临时存储正在创建的Bean实例。这三个缓存分别是singletonObjects、earlySingletonObjects和singletonFactories。

  1. singletonObjects:这是最终的单例缓存,用于存储完全初始化的Bean实例。当Bean的所有依赖已经注入并且初始化完成后,Bean将被放入singletonObjects缓存中。
  2. earlySingletonObjects:这是早期的单例缓存,用于存储尚未完全初始化的Bean实例。当Bean正在创建但尚未完成初始化时,Bean将暂时存储在earlySingletonObjects缓存中。
  3. singletonFactories:这是存储用于创建Bean实例的ObjectFactory的缓存。当创建Bean实例时,会使用ObjectFactory来延迟创建Bean,将ObjectFactory存储在singletonFactories缓存中。

三级缓存的作用?

三级缓存的作用是解决Spring中的循环依赖问题。

当两个或多个Bean相互依赖时,Spring会使用三级缓存来确保每个Bean都能够被正确地初始化,并且避免出现死锁或无限循环等问题。三级缓存机制允许Spring容器在创建Bean时暂时存储正在创建的Bean实例,以便在循环依赖的情况下能够正确地解析Bean的依赖关系,并最终完成Bean的初始化。


强烈推荐

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站:人工智能

b004071ozy_05_amzn

专栏集锦

大佬们可以收藏以备不时之需:

Spring Boot 专栏:http://t.csdnimg.cn/peKde

ChatGPT 专栏:http://t.csdnimg.cn/cU0na

Java 专栏:http://t.csdnimg.cn/YUz5e

Go 专栏:http://t.csdnimg.cn/Jfryo

Netty 专栏:http://t.csdnimg.cn/0Mp1H

Redis 专栏:http://t.csdnimg.cn/JuTue

Mysql 专栏:http://t.csdnimg.cn/p1zU9

架构之路 专栏:http://t.csdnimg.cn/bXAPS


写在最后

感谢您的支持和鼓励! 本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/917473

推荐阅读
相关标签