Spring如何解决项目中的循环依赖问题?

目录

什么是循环依赖?

如何解决?

采用两级缓存解决

需要AOP的Bean的循环依赖问题?

三级缓存解决


什么是循环依赖?

循环依赖就是Spring在初始化Bean时两个不同的Bean你依赖我,我依赖你的情况

例如A依赖B,B依赖A,当IoC容器初始化A时,发现它依赖于B,然后去创建B,创建B的时候又发现B依赖于A,而容器中不存在A,如果不想办法解决,这就会陷入死循环

例如下面这个代码,就是典型的循环依赖

java 复制代码
// Spring 配置类,启用组件扫描
@Configuration
@ComponentScan
class AppConfig {
}

// Author 服务类,依赖 BookService
@Service
class AuthorService {
    @Autowired
    BookService bookService;
}

// Book 服务类,依赖 AuthorService
@Service
class BookService {
    @Autowired
    AuthorService authorService;
}

// 程序入口类,测试循环依赖注入
public class SpringCircularDependencySingleClass {
    public static void main(String[] args) {
        ApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        BookService bookService = (BookService) annotationConfigApplicationContext.getBean("bookService");
        System.out.println(bookService.authorService);

        AuthorService authorService = (AuthorService) annotationConfigApplicationContext.getBean("authorService");
        System.out.println(authorService.bookService);
    }
}    

运行这个项目,会发现它是正常运行的,这就表示Spring在背后做了一些工作解决了循环依赖的问题

如何解决?

Spring是使用三级缓存的模式来解决循环依赖问题的,其实两级缓存也是能够解决一般的循环依赖问题的,三级缓存主要是为了更好的解决带有AOP的Bean的循环依赖情况

这里先用两级缓存结构来初步阐述思想

采用两级缓存解决

Spring解决循环依赖的关键在于巧妙利用缓存机制。在Spring的实现中,主要涉及两个重要的Map:

singletonObjects :这是一个单例池,存放的是经历了完整Spring生命周期的Bean实例。这些Bean的所有依赖都已成功填充,处于完全可用状态。

earlySingletonObjects :该Map用于存放提前暴露出来的Bean对象。这些Bean刚刚创建完成,但尚未经历完整的Spring生命周期,其依赖尚未填充完毕

假设此时有两个Bean分别叫A、B,它们互相依赖

解决循环依赖的具体步骤如下:

1.先创建A的实例对象,在A的依赖注入之前,将A的早期对象放入earlySingletonObjects中去,然后开始给A注入依赖,发现依赖于B(此时两级缓存中都没有B),于是转去创建B

2.创建B的实例对象,将B的早期对象放入**earlySingletonObjects,**然后给B注入依赖,发现B依赖于A,于是去缓存中查找符合条件的Bean

3.先从singletonObjects 中查找,发现没有,然后去earlySingletonObjects查找,发现存在A的早期对象,于是返回这个早期对象

4.将A注入到B中,然后将B放入singletonObjects中去

5.这个时候A可以从singletonObjects 中获取B的实例,然后将完整的A放入singletonObjects中,循环依赖问题得以解决

需要AOP的Bean的循环依赖问题?

当一个Bean需要使用增强时,我们需要的是它的代理对象而不是它的原型对象,这个时候简单的两级缓存结构并不能很好的解决这个问题,所以Spring选择了三级缓存结构来解决

(两级缓存的解决思路是,在将早期对象放入earlySingletonObjects 中前,先判断一下该对象是否需要AOP,如果需要的话生成该对象的代理对象放入earlySingletonObjects

但是!Spring的理念是尽量的把AOP的部分放到构造Bean的后期解决,而不是上来就直接生成一个代理对象,而且过早的生成代理对象也会额外造成空间和时间的消耗)

三级缓存解决

Spring引入了一个新的Map singletonFactories 来解决这个问题。 singletonFactories 存放的是 ObjectFactory 类型的工厂方法。当创建完对象后,并不立即进行AOP增强,而是将获取该对象的工厂方法放入 singletonFactories 。当发生循环依赖需要获取对象时,如果从 earlySingletonObjects 中无法获取到合适的对象,就从 singletonFactories 中取出工厂方法并执行,从而获取经过AOP增强后的对象。

相关推荐
tellmewhoisi4 分钟前
java8 List常用基本操作(去重,排序,转换等)
java·list
Lemon程序馆8 分钟前
今天聊聊 Mysql 的那些“锁”事!
后端·mysql
龙卷风040510 分钟前
使用本地IDEA连接服务器远程构建部署Docker服务
后端·docker
vv安的浅唱15 分钟前
Golang基础笔记七之指针,值类型和引用类型
后端·go
陪我一起学编程26 分钟前
MySQL创建普通用户并为其分配相关权限的操作步骤
开发语言·数据库·后端·mysql·oracle
都叫我大帅哥33 分钟前
TOGAF应用架构阶段全解析:从理论到Java代码实战
java
Amagi.1 小时前
Java设计模式-建造者模式
java·设计模式·建造者模式
EmpressBoost1 小时前
谷粒商城170缓存序列化报错
java·spring·缓存
fouryears_234171 小时前
@PathVariable与@RequestParam的区别
java·spring·mvc·springboot