NOTE

2.54 循环依赖

1. 循环依赖是什么 假设有两个Bean,Bean1和Bean2,如果Bean1依赖Bean2,Bean2依赖Bean1。 那么Spring创建Bean1的时候需要先创建Bean2,创建Bean2又需要先创建Bean1,这就叫做循环依赖 2. 循环依赖会导致什么问题 2.1. 构造器注入会抛出异常

Java创建于 更新于 historical

这是历史学习笔记,可能存在过时或不完整的理解。

1. 循环依赖是什么

假设有两个Bean,Bean1和Bean2,如果Bean1依赖Bean2,Bean2依赖Bean1。 那么Spring创建Bean1的时候需要先创建Bean2,创建Bean2又需要先创建Bean1,这就叫做循环依赖

2. 循环依赖会导致什么问题

2.1. 构造器注入会抛出异常

  • TestController
@Service
public class TestController
{
    private TestService testService;

    @Autowired
    public TestController(TestService testService)
    {
        this.testService = testService;
    }

    public void test()
    {
        String byId = testService.getById(1);
        System.out.println(byId);
    }
}
  • TestService
@Service
public class TestService
{
    private TestController testController;

    @Autowired
    public TestService(TestController testController)
    {
        this.testController = testController;
    }

    public String getById(Integer id)
    {
        return "" + id;
    }
}

启动后会抛出异常:

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  testController defined in file [C:\Users\[user]\code\test\target\classes\com\zsk\test\controller\TestController.class]
↑     ↓
|  testService defined in file [C:\Users\[user]\code\test\target\classes\com\zsk\test\service\TestService.class]
└─────┘



Process finished with exit code 1

3. 如何解决循环依赖的问题

3.1. 属性/setter方法注入

  • TestController
@Service
public class TestController
{
    @Autowired//依赖TestService
    private TestService testService;

    public void test()
    {
        String byId = testService.getById(1);
        System.out.println(byId);
    }
}
  • TestService
@Service
public class TestService
{
    @Autowired//依赖TestController
    private TestController testController;

    public String getById(Integer id)
    {
        return "" + id;
    }
}

3.1.1. 原理分析

Spring有三个步骤,

  1. createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
  2. populateBean:填充属性,这一步主要是多bean的依赖属性进行填充
  3. initializeBean:调用spring xml中的init 方法。一个创建实例,另一个对属性赋值(属性的注入就是在这个步骤进行)。

用上面的例子说明,Spring创建TestController的时候:

  • createBeanInstance创建TestController实例,放到map缓存中
  • populateBean对TestController的属性TestService赋值的时候,发现没有TestService实例,因此先要创建TestService实例
    • createBeanInstance创建TestService实例
    • populateBean对TestService的属性TestController赋值的时候,容器中已经有了TestController实例,那么从map缓存中取出来赋值
  • 继续回到populateBean对TestController的属性TestService赋值即可

3.2. 使用@PostConstruct

3.3. 实现ApplicationContextAware与InitializingBean

3.4. 在构造函数上都加上@Lazy注解

  • TestController
@Service
public class TestController
{
    private TestService testService;

    @Lazy
    @Autowired
    public TestController(TestService testService)
    {
        this.testService = testService;
    }

    public void test()
    {
        String byId = testService.getById(1);
        System.out.println(byId);
    }
  • TestService
@Service
public class TestService
{
    private TestController testController;

    @Autowired
    @Lazy
    public TestService(TestController testController)
    {
        this.testController = testController;
    }

    public String getById(Integer id)
    {
        return "" + id;
    }
}

4. 参考