springboot3 循环依赖配置_spring


1,什么是循环依赖?

发生在bean A依赖于另一个bean B时,bean B依赖于bean A;

2,Sping中发生了什么?

当Spring上下文加载所有bean时,它会尝试按照它们完全工作所需的顺序创建bean。例如,如果我们没有循环依赖,如下例所示:

A->B->C

Spring将创建bean C,然后创建bean B(并将bean注入其中),然后创建bean A(并将bean B注入其中)。

但是,当具有循环依赖时,Spring无法决定应该首先创建哪个bean,因为它们彼此依赖。在这些情况下,Spring将在加载上下文时引发BeanCurrentlyInCreationException。

使用构造函数注入时,它可能发生在Spring中; 如果您使用其他类型的注入,则不应该发现此问题,因为依赖项将在需要时注入,而不是在上下文加载时注入。

3 Demo

让我们定义两个相互依赖的bean(通过构造函数注入)

package com.evan.springboot.study;

import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author evanYang
 * @version 1.0
 * @date 2020/05/19 21:46
 */
@Component
@NoArgsConstructor
public class DependecyA {
    private DependecyB dependecyB;

    @Autowired
    public DependecyA(DependecyB dependecyB){
        this.dependecyB=dependecyB;
    }
}
package com.evan.springboot.study;

import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author evanYang
 * @version 1.0
 * @date 2020/05/19 21:46
 */
@Component
@NoArgsConstructor
public class DependecyB {

    private DependecyA dependecyA;

    @Autowired
    public DependecyB(DependecyA dependecyA){
        this.dependecyA=dependecyA;
    }
}

现在我们可以为测试编写一个Configuration类,让我们称之为DemoConfig ,它指定要扫描组件的基础包。假设我们的bean在包“ com.evan.springboot.study ” 中定义:

package com.evan.springboot.study;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author evanYang
 * @version 1.0
 * @date 2020/05/19 21:48
 */
@Configuration
@ComponentScan(basePackages = {"com.evan.springboot.study"})
public class DemoConfig {
}

最后我们可以编写一个JUnit测试来检查循环依赖。测试可以为空,因为在上下文加载期间将检测循环依赖性。

package com.evan.springboot;

import com.evan.springboot.study.DemoConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @author evanYang
 * @version 1.0
 * @date 2020/05/19 21:49
 */
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = { DemoConfig.class })
public class Demo {
    @Test
    public void demoTest() {

        // Empty test; we just want the context to load

    }
}

springboot3 循环依赖配置_ci_02

4解决方案

4.2。使用@Lazy
打破循环的一个简单方法是让Spring懒洋洋地初始化其中一个bean。那就是:它不是完全初始化bean,而是创建一个代理将它注入另一个bean。注入的bean只有在第一次需要时才会完全创建。

要使用我们的代码尝试此操作,您可以将DependecyA更改为以下内容:

@Component

public class DependecyA {

 

    private DependecyB circB;

 

    @Autowired

    public DependecyA(@Lazy DependecyB circB) {

        this.circB = circB;

    }

}

4.3。使用Setter / Field Injection
最流行的解决方法之一,也是Spring文档提出的,是使用setter注入。

简单地说,如果你改变你的bean的连接方式,使用setter注入(或现场注入)而不是构造函数注入 - 这确实解决了这个问题。这样Spring就会创建bean,但是在需要之前不会注入依赖项。

让我们这样做 - 让我们改变我们的类以使用setter注入,并将另一个字段(消息)添加到CircularDependencyB,以便我们可以进行适当的单元测试:

@Component

public class DependecyA {

 

    private DependecyB circB;

 

    @Autowired

    public void setCircB(DependecyB circB) {

        this.circB = circB;

    }

 

    public DependecyB getCircB() {

        return circB;

    }

}

4.4。使用@PostConstruct
打破循环的另一种方法是在其中一个bean上使用@Autowired注入依赖项,然后使用@PostConstruct注释的方法来设置其他依赖项。

我们的bean可以有以下代码:

@Component

public class DependencyA {

 

    @Autowired

    private DependencyB circB;

 

    @PostConstruct

    public void init() {

        circB.setCircA(this);

    }

 

    public DependencyB getCircB() {

        return circB;

    }

}