一、写在前面
上回书说到,spring中自动装配的关键注解@Autowired的原理,今天来给大家分享一下spring中的循环依赖问题以及该如何解决。我们之前写项目用到的mvc框架以及控制层、业务层、数据访问层三层架构的形式来编写项目,一环套一环,基本不会有什么问题,但在特殊情形下,确实可能出现循环依赖的问题,首先来解释一下什么是循环依赖。
二、循环依赖
1.定义:
循环依赖的定义很容易理解,假如有两个类:Demo01,Demo02,
代码如下:
@Component
public class Demo01 {
private Demo02 circB;
@Autowired
public Demo01(Demo02 circB) {
this.circB = circB;
}
}
@Component
public class Demo02 {
@Autowired
private Demo01 circA;
public Demo02(Demo01 circA) {
this.circA = circA;
}
}
然后再写一个配置类:
@Configuration
@ComponentScan(basePackages = { "com.example.demo.pojo" })
public class TestConfig {
}
可以看到,Demo01和Demo02互相依赖了对方,如果这样测试会发生什么呢
测试类:
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestConfig.class })
public class DemoApplicationTests {
@Test
public void ceshi() {
}
}
在进行测试的时候,会弹出这样的报错:
意思是在 Demo01需要通过构造函数注入的Demo02实例(或者Demo02中声明的Bean),而Demo02需要通过构造函数注入的 Demo01的实例(或者 Demo01中声明的Bean)。如果将 Demo01和类Demo02的bean配置为相互注入,则Spring IoC容器会在运行时检测到此循环引用,并引发一个BeanCurrentlyInCreationException。与典型情况(没有循环依赖)不同,bean Demo01和bean Demo02之间的循环依赖关系迫使其中一个bean在被完全初始化之前被注入到另一个bean中。
听起来有点难以理解,但其实不难理解,我在做项目的时候,通常会有这样一种思想,把代码以及代码的运行想象成一条管道,首先管道通向哪里要明确,其次管道中的阻塞需要排除,而依赖注入就好像两个或者多个代码抢一个管道,毕竟spring框架又不是人,没法做到调度精确,所以如何解决循环依赖,就需要我们去想想办法。
2.解决方法
去网上查一查,发现有很多解决这问题的方法,我就挑一些简单易懂的来聊聊。
惹不起还躲不起了,把设计项目的人拉出来打一顿,或者把项目的层次结构设计地完善一点,不要出现类互相依赖,循环依赖的现象。当然这种方法只适合在项目初期以及你打得过项目经理的前提下。
2.2使用@Lazy
打破循环的一个简单方法是让Spring懒洋洋地初始化其中一个bean。那就是:它不是完全初始化bean,而是创建一个代理将它注入另一个bean。注入的bean只有在第一次需要时才会完全创建。
package com.example.demo.pojo;/*
@Author 涂碧宇写的代码
@since 2022/9/26
@version 1.0
*/
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class Demo01 {
@Autowired
private Demo02 circB;
public Demo01( @Lazy Demo02 circB) {
this.circB = circB;
}
}
测试发现:
完美~