NOTE
2.54 循环依赖
1. 循环依赖是什么 假设有两个Bean,Bean1和Bean2,如果Bean1依赖Bean2,Bean2依赖Bean1。 那么Spring创建Bean1的时候需要先创建Bean2,创建Bean2又需要先创建Bean1,这就叫做循环依赖 2. 循环依赖会导致什么问题 2.1. 构造器注入会抛出异常
这是历史学习笔记,可能存在过时或不完整的理解。
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有三个步骤,
- createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
- populateBean:填充属性,这一步主要是多bean的依赖属性进行填充
- 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;
}
}