一、循环依赖概念
在Spring应用中,循环依赖指的是两个或多个Bean之间相互引用,造成了一个环状的依赖关系。举例来说,如果Bean A依赖于Bean B,同时Bean B也依赖于Bean A,就形成了循环依赖。这种情况下,Spring容器在创建这些Bean时会陷入无限循环,导致应用启动失败或者出现其他不可预测的问题。
二、解决方案(三级缓存)
Spring容器在解决循环依赖问题时使用了三级缓存的机制。在创建Bean的过程中,Spring容器会利用三个缓存来处理实例化和依赖注入,确保即使在存在循环依赖的情况下也能正确创建Bean。
1、Spring三级缓存流程
让我们深入了解一下这个过程:
- 当 Spring IOC 容器扫描到一个 Bean 时,它会先将其实例化并放入一级缓存中。同时,会为这个 Bean 创建一个
ObjectFactory
对象,用于后续的依赖注入。 - 如果 Bean 依赖其他 Bean,Spring 在创建 Bean 实例时,会先检查一级缓存。如果 Bean 还不存在于一级缓存中,Spring 开始实例化该 Bean,并将其添加到三级缓存中。这时,Bean 具有自己的内存地址。
- 接下来,Spring 填充 Bean 的属性。如果属性依赖于其他 Bean,Spring 会从一级缓存中获取对应的 Bean。如果 Bean 不存在于一级缓存中,Spring 会从三级缓存中获取
ObjectFactory
,执行工厂方法,创建 Bean 的早期引用,并将其放入二级缓存中。 - 在回溯过程中,当 Bean 成为“成品”时,它会从三级缓存中移除,并放入一级缓存中。这样,Bean 的初始化就完成了。
- 最终,所有 Bean 都进入一级缓存,准备供用户使用。
2、源码分解
Spring框架的IoC容器是Java开发者广泛使用的组件之一,它通过控制反转的方式管理Bean的生命周期。Spring在创建Bean的过程中,为了避免不必要的性能开销,引入了多级缓存机制。本文将从源码的角度分析Spring是如何创建Bean的,重点探讨Spring中缓存的使用。
Step1: Spring容器的初始化
Spring容器的初始化始于AbstractApplicationContext的refresh()
方法的调用,该方法触发了容器的刷新操作。 AbstractApplicationContext是Spring加载上下文的入口。
org.springframework.context.support.AbstractApplicationContext#refresh()
这里最关键的步骤是obtainFreshBeanFactory(),它会创建一个新的DefaultListableBeanFactory,这个Factory在后续会完成具体的bean加载和创建工作。
Step2: Bean的定义和注册
在前面的obtainFreshBeanFactory()方法中,Spring会加载所有的BeanDefinition,这些BeanDefinition可能来自XML文件、注解扫描等。而加载BeanDefinition的过程,就是调用DefaultListableBeanFactory的registerBeanDefinition方法。
org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
registerBeanDefinition方法就是将加载的BeanDefinition存储到一个内部Map中,留待后续使用。
Step3: Bean的获取与创建
Step3-1:Bean的获取getBean()
当我们需要使用某个bean时,就会调用AbstractBeanFactory的getBean(),它是获取bean的入口。
org.springframework.beans.factory.support. AbstractBeanFactory#getBean()
Spring在获取一个bean实例时,首先会检查这个bean是否已存在于单例缓存容器singletonObjects中。
如果缓存存在,直接返回缓存的bean实例,避免重复创建。如果缓存不存在,则调用createBean方法创建一个新的bean实例。
@Override
public Object getBean(String name) throws BeansException {
// 检查单例缓存
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null) {
// 缓存存在,直接返回
return sharedInstance;
}
// 缓存不存在,则创建bean
return createBean(beanName, ...);
}
Step3-2:获取缓存中的Bean(getSingleton)
Spring会将创建的单例对象存入singletonObjects单例缓存中,确保下次获取该bean时可以直接从缓存中获取。
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton
如果单例缓存中不存在,就会尝试从单例缓存的子逻辑缓存中获取:
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 检查是否在创建中
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 尝试从earlySingletonObjects中获取,earlySingletonObjects属于二级缓存
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 从三级缓存singletonFactories中获取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 将三级缓存获取到的bean放入earlySingletonObjects二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
}
}
}
}
return singletonObject;
}
这里可以看到,Spring维护了三级缓存:
- singletonObjects: 单例bean缓存
- earlySingletonObjects: 早期曝光的单例bean缓存
- singletonFactories: 单例bean工厂缓存
当bean不存在于singletonObjects一级缓存时,Spring会先检查该bean是否正在创建中,如果是则尝试从earlySingletonObjects二级缓存获取。如果二级缓存也没有,那么就从singletonFactories三级缓存中获取。这样设计三级缓存主要是为了解决循环依赖的问题。
Step3-3:Bean的创建createBean
在AbstractAutowireCapableBeanFactory的createBean方法中,会先尝试从缓存中获取该bean,如果缓存不存在,才会正式调用createBeanInstance方法实例化该bean。
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean
createBean方法是创建bean的核心逻辑,包括了bean的实例化、属性注入、循环依赖处理、初始化方法回调等。
需要重点关注doCreateBean和populateBean两个方法。
doCreateBean方法的主要工作是实例化bean对象。Spring通过不同的InstantiationStrategy策略来实例化不同的bean,如反射创建、通过工厂方法创建、CGLIB创建等。
populateBean方法会完成属性注入的工作。包括依赖注入、自动装配和循环依赖的处理。循环依赖是Spring处理较为复杂的一个环节,通过三级缓存来完成。
Step3-4:创建的Bean存入缓存
进入doCreateBean方法真正地创建bean实例。
在AbstractAutowireCapableBeanFactory中,创建单例bean后,是通过调用addSingletonFactory方法将bean添加到三级缓存中。
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory
至此spring bean创建与缓存的过程就差不多完成了。
3、三级缓存的必要性
Spring三级缓存解决循环依赖的一个重要原因是确保带有AOP及其它增强的Bean在返回给其他Bean之前必须完全初始化。因为AOP增强通常涉及到代理的创建,这需要在Bean的所有依赖都解决之后进行。如果提前返回一个未完成增强的Bean实例,那么它可能不会按预期工作,因为它缺少了如事务管理、安全检查等关键行为。
三级缓存中的singletonFactories存储的工厂对象允许Spring在Bean完全初始化并应用了所有AOP及其它增强之后,再返回Bean的实例。这样,即使在循环依赖的情况下,也能确保每个Bean都是完整且正确增强的,从而保持了应用的一致性和稳定性。