在Spring框架中,Bean的循环依赖是一个常见的问题,它指的是两个或多个Bean之间通过构造函数、Setter方法或字段注入等方式形成了相互依赖的闭环。Spring框架提供了强大的依赖注入功能,同时也提供了多种机制来处理循环依赖的情况,确保应用能够正确启动和运行。
要进行了解spring的循环依赖,我们先来进行看下spring的生命周期
1.Spring Bean生命周期概述
spring的生命周期主要包含以下几个阶段
• 容器启动阶段
•Bean对象实例化阶段
•bean属性的注入阶段
•bean的初始化阶段
•bean缓存阶段
•bean销毁阶段
2.生命周期各阶段详解
2.1 容器启动阶段
我们程序员通过XML配置或Java配置(如使用@Configuration
注解的类)定义Spring应用的上下文配置。配置中指定了组件扫描的包路径(通过@ComponentScan
或<context:component-scan>
)
扫描和实例化BeanDefinition对象。并将BeanDefinition对象放入到BeanDefinitionMap中。注册BeanPostProcessor,验证BeanDefinition对象是否合格。
2.2 Bean对象实例化阶段
推测Bean对象的实例化方法,并进行实例化操作。
2.3 bean属性的注入阶段
查找注入信息,完成注解解析和属性注入
2.4 bean的初始化阶段
各种Aware的回调,各种初始化方法的回调,对bean的扩展例如aop
2.5 bean缓存阶段
各种临时变量的删除操作,缓存到单例池中
2.6 bean销毁阶段
销毁方法的回调,移除单例池。
3.创建bean方法的主要逻辑
4.循环依赖
spring中的循环依赖主要是依赖的是三级缓存。这个三级缓存是我们程序员给取的名字,并不是spring官方起的名字。这个三级缓存分别是singletonObjects(一级缓存),earlySingletonObjects(二级缓存),singletonFactories(三级缓存)。
singletonObjects:也称作单例池,主要是存放的最终形态的单例bean,一般获取一个bean都是从这个集合中进行获取。
singletonFactories:二级缓存,主要存放的是ObjectFactory,作用主要是为了进行创建一个对象,这个缓存中存放的是一个工厂。
earlySingletonObjects:三级缓存,主要进行存放的是过渡的bean
假设我们现在有两个类 一个M类 一个N类,M依赖N,N同时也依赖M
@Component
public class N {
@Autowired
private N n;
}
@Component
public class M {
@Autowired
private N n;
}
我们接下来进行分析一下创建M,N类的时候,这三个集合的数据变化的逻辑 这里指进行分析M,N这两个对象的情况,spring内置的那些就不在考虑的范围内。
M类和N类的循环依赖流程如下所示:
1.首先我们进行M对象的创建操作,当创建M对象执行到populateBean方法的时候,此时的各个集合的情况如下:
2.当进行执行populateBean方法需要对M里的n属性进行复制的时候,会从容器中进行获取n 此时发现容器中不存在n属性,那么就会接着走上面进行创建bean的流程。这个也会走到populateBean方法,此时这三个集合的情况就如下所示:
3.在给N类的m属性进行赋值的时候,需要进行获取m属性值的时候这个时候会进行调用getSingleton方法。里面进行删除了一些代码。
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从单例池中获取对象信息
Object singletonObject = this.singletonObjects.get(beanName);
//此时进行设置N类中的m对象 这个时候从单例池中获取m对象发现对象为空
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从二级缓存中进行获取m对象,发现缓存中也没有对象信息
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//从三级缓存中进行进行获取factory工厂
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//factory工厂不为null 进行创建对象信息
singletonObject = singletonFactory.getObject();
// 将创建好的对象信息 存放到二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
//三级缓存中删除工厂类对象
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
这个时候的三个集合就会发生变化,变化之后的情况如下:
4. N类初始化完成之后的情况
5.问题与反思
为何需要三级缓存,二级缓存(singletonFactories)只进行存放一个半成品的bean为何不可以。二级缓存(singletonFactories)有什么用呢,二级缓存(singletonFactories)中存放的是一个工厂类,这个工厂类可以进行创建对象,那spring为啥要在二级缓存(singletonFactories)中存放一个工厂呢,这块有什么别的深意呢,笔者目前认为这块是在spring Aop的时候可以通过二级缓存(singletonFactories)进行创建一个代理对象。