在 Spring 中,循环依靠(Circular Dependency)指的是两个或多个 Bean 之间相互依靠,形成了一个循环依靠的联系。当呈现循环依靠时,Spring 需求经过一些特别的技术手段来处理这个问题,保证 Bean 的正确创立和初始化。下面咱们一起经过理论再结合源码一起推导出spring循环依靠底层真想。达到对Spring 循环依靠的底层完结原理的深度剖析。
1. 循环依靠的问题
循环依靠问题的呈现原因是因为在创立 Bean 的进程中,Bean 之间相互依靠,导致 Bean 的创立顺序不确定,然后无法保证一切的 Bean 都被正确地创立和初始化。例如,假设有两个 Bean A 和 B,它们都需求依靠对方才能完结初始化:
- 咱们先经过一个简略示例引出问题
@Component
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
public B(A a) {
this.a = a;
}
}
在这种情况下,假如直接运用 Spring 的规范依靠注入办法,创立 A 和 B 的时分会呈现循环依靠的问题,导致程序无法正确运行。
2. 处理循环依靠的办法
Spring 处理循环依靠的办法是经过提前露出半成品目标(Early-Stage Object)来处理。当 Spring 创立一个 Bean 的时分,它会先创立该 Bean 的半成品目标,然后再注入该 Bean 所依靠的其他 Bean。当一切的 Bean 都被创立并注入完结后,Spring 再完结这些半成品目标的初始化,然后处理了循环依靠的问题。
Spring 处理循环依靠的进程大致分为三个步骤:
- 创立 Bean 的半成品目标,并将其添加到缓存中。
- 注入该 Bean 所依靠的其他 Bean。
- 完结 Bean 的初始化,将半成品目标转换为完好的 Bean 目标。
下面是 Spring 处理循环依靠的详细完结原理。
3. Spring 循环依靠的完结原理
Spring 的循环依靠处理方案主要依靠于两个技术:BeanPostProcessor和三级缓存。
BeanPostProcessor
BeanPostProcessor 是 Spring 中的一个接口,它供给了两个办法:postProcessBeforeInitialization
和postProcessAfterInitialization
。这两个办法分别在 Bean 的初始化前后被调用,能够用来对 Bean 进行定制化处理。
在处理循环依靠问题时,Spring 运用 BeanPostProcessor 在 Bean 初始化之前对 Bean 进行处理,然后完结提前露出半成品目标的目的。
– 三级缓存
Spring 中的 BeanFactory 是一个三级缓存结构,其中包含了singletonObjects、earlySingletonObjects 和 singletonFactories 三个缓存
。
当 Spring 创立一个 Bean 的时分,它会先查看 singletonObjects 缓存中是否存在该 Bean 的实例。假如存在,直接回来该实例;不然持续创立该 Bean 的实例。
假如在创立该 Bean 的进程中呈现了循环依靠,Spring 会将该 Bean 的半成品目标存储在 earlySingletonObjects 缓存中,并将其标记为“当时正在创立的 Bean”,然后持续创立该 Bean 所依靠的其他 Bean。当一切的 Bean 都被创立完结后,Spring 会调用 BeanPostProcessor 的postProcessAfterInitialization
办法,将一切标记为“当时正在创立的 Bean”的半成品目标转化为完好的 Bean 目标,并存储在 singletonObjects 缓存中。
假如在创立该 Bean 的进程中需求调用其他 Bean 的工厂办法,则 Spring会将该 Bean 的工厂办法存储在 singletonFactories 缓存中,以便在创立其他 Bean 时运用。当一切的 Bean 都被创立完结后,Spring 会遍历 singletonFactories 缓存中的一切工厂办法,调用它们的 getObject() 办法,将其转换为完好的 Bean 目标,并存储在 singletonObjects 缓存中。
经过运用三级缓存和 BeanPostProcessor,Spring 能够在 Bean 的创立进程中处理循环依靠问题,并保证一切的 Bean 都被正确地创立和初始化。
4. 循环依靠的限制
虽然 Spring 能够处理循环依靠问题,可是它也有一些限制:
- 循环依靠只适用于 singleton 效果域的 Bean。对于 prototype 效果域的 Bean,Spring 无法处理循环依靠问题。
- 循环依靠只适用于 constructor 和 setter 注入办法。对于其他的注入办法,如字段注入或办法注入,Spring 无法处理循环依靠问题。
总归,Spring 的循环依靠处理方案是经过 BeanPostProcessor 和三级缓存完结的。当呈现循环依靠时,Spring 会先创立 Bean 的半成品目标,并将其添加到 earlySingletonObjects 缓存中,然后持续创立该 Bean 所依靠的其他 Bean。当一切的 Bean 都被创立完结后,Spring 会调用 BeanPostProcessor 的postProcessAfterInitialization
办法,将一切标记为“当时正在创立的 Bean”的半成品目标转化为完好的 Bean 目标,并存储在 singletonObjects 缓存中。虽然 Spring 能够处理循环依靠问题,可是它也有一些限制,需求在运用时留意。
此外,需求留意的是,循环依靠可能会导致性能问题。当存在很多的循环依靠联系时,Spring 需求创立很多的半成品目标和缓存,然后占用很多的内存和 CPU 资源。因此,在规划应用程序时,需求尽量避免循环依靠的呈现。
最后,假如呈现循环依靠的问题,主张经过重构代码的办法来处理,尽量减少 Bean 之间的相互依靠联系。假如无法避免循环依靠,能够考虑运用其他依靠注入框架,或许手动管理 Bean 的创立和初始化进程,以避免循环依靠的问题。
好的,下面咱们来看一下 Spring 源码中是怎么处理循环依靠的问题的。
5.源码解析
好的,下面是对 Spring 源码中处理循环依靠问题的关键部分进行注释的代码:
// AbstractApplicationContext.java
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {
public void refresh() throws BeansException, IllegalStateException {
// 创立 BeanFactory,并经过 BeanFactoryPostProcessor 对 BeanFactory 进行处理
refreshBeanFactory();
// 经过 BeanDefinitionReader 将 Bean 的界说信息读取到 BeanFactory 中,并注册到 BeanDefinitionMap 中
// 然后,经过 DefaultListableBeanFactory 中的 preInstantiateSingletons() 办法创立 Bean 实例
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// ...
// 创立 Bean 的进程是由 getBean() 办法触发的
// 在 getBean() 办法中,会先查看 singletonObjects 缓存中是否存在该 Bean 的实例
// 假如存在,直接回来该实例;不然持续创立该 Bean 的实例
beanFactory.getBean(beanName);
// ...
}
}
// DefaultListableBeanFactory.java
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry {
public Object getBean(String name) throws BeansException {
// ...
// 先查看 singletonObjects 缓存中是否存在该 Bean 的实例
// 假如存在,直接回来该实例;不然持续创立该 Bean 的实例
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null) {
// ...
return null;
}
// ...
// 创立该 Bean 的半成品目标,并将其存储在 earlySingletonObjects 缓存中
// 然后,持续创立该 Bean 所依靠的其他 Bean
// 当一切的 Bean 都被创立完结后,会调用 BeanPostProcessor 的 postProcessAfterInitialization() 办法
// 将一切标记为“当时正在创立的 Bean”的半成品目标转化为完好的 Bean 目标,并存储在 singletonObjects 缓存中
createBean(beanName, mbd, args);
// ...
}
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) {
// ...
// 创立该 Bean 的半成品目标,并将其存储在 earlySingletonObjects 缓存中
Object beanInstance = doCreateBean(beanName, mbd, args);
// ...
// 调用 BeanPostProcessor 的 postProcessAfterInitialization() 办法
// 将一切标记为“当时正在创立的 Bean”的半成品目标转化为完好的 Bean 目标,并存储在 singletonObjects 缓存中
// 在 postProcessAfterInitialization() 办法中,会查看该 Bean 是否存在循环依靠的问题
Object exposedObject = bean;
if (mbd.isSingleton()) {
// ...
// 将该 Bean 存储在 singletonObjects 缓存中
addSingleton(beanName, singletonObject);
// ...
}
// ...
}
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// ...
// 假如创立依靠的 Bean 时发现循环依靠的问题
// 会先将该 Bean 的工厂办法存储在 singletonFactories 缓存中,以便在创立其他 Bean 时运用
if (mbd.isPrototype()) {
// ...
} else {
// ...
// 假如存在循环依靠的问题
if (isSingletonCurrentlyInCreation(beanName)) {
// 先从 singletonFactories 缓存中获取该 Bean 的工厂办法
ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
if (singletonFactory != null) {
// ...
// 将该 Bean 存储在 singletonObjects 缓存中
addSingleton(beanName, singletonObject);
// ...
}
}
// ...
}
// ...
}
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
// ...
// 在对特点进行赋值的进程中,假如发现循环依靠的问题
// 会先将该 Bean 的工厂办法存储在 singletonFactories 缓存中
if (hasInstantiationAwareBeanPostProcessors()) {
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
// 假如该 Bean 存在循环依靠的问题
// 则先将该 Bean 的工厂办法存储在 singletonFactories 缓存中
// 然后,将该 Bean 标记为“当时正在创立的 Bean”
// 最后,调用 BeanPostProcessor 的 postProcessBeforeInstantiation() 办法
// 创立该 Bean 的工厂办法,并将其存储在 singletonFactories 缓存中
// 在 postProcessBeforeInstantiation() 办法中,会回来该 Bean 的工厂办法
// 以便在创立其他 Bean 时运用
Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
// ...
// 将该 Bean 的工厂办法存储在 singletonFactories 缓存中
singletonFactories.put(beanName, () -> result);
// ...
return result;
}
}
}
// ...
}
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
// ...
// 在对特点进行赋值的进程中,假如发现循环依靠的问题
// 会先将该 Bean 的工厂办法存储在 singletonFactories 缓存中
if (hasInstantiationAwareBeanPostProcessors()) {
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
// ...
// 假如该 Bean 存在循环依靠的问题
// 则先将该 Bean 的工厂办法存储在 singletonFactories 缓存中
// 然后,将该 Bean 标记为“当时正在创立的 Bean”
// 最后,调用 BeanPostProcessor 的 postProcessPropertyValues() 办法
// 对该 Bean 的特点进行赋值,并回来新的 PropertyValues 目标
// 在 postProcessPropertyValues() 办法中,会查看该 Bean 是否存在循环依靠的问题
PropertyValues pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bean, beanName);
if (pvsToUse == null) {
// ...
} else {
// ...
pvs = pvsToUse;
}
}
}
// ...
}
}
6.总结
-
在 Spring 中,创立 Bean 的进程是由AbstractApplicationContext类中的 refresh() 办法触发的。在 refresh() 办法中,会先创立 BeanFactory,并经过 BeanFactoryPostProcessor 对 BeanFactory 进行处理。然后,经过 BeanDefinitionReader将 Bean的界说信息读取到 BeanFactory 中,并注册到BeanDefinitionMap中。接着,经过DefaultListableBeanFactory中的 preInstantiateSingletons() 办法创立 Bean 实例。
-
在 DefaultListableBeanFactory 中,Bean 的创立进程是由 getBean() 办法触发的。在 getBean() 办法中,会先查看 singletonObjects 缓存中是否存在该Bean的实例。假如存在,直接回来该实例;不然持续创立该 Bean 的实例。在创立该 Bean 的进程中,假如存在循环依靠的问题,会先创立 Bean 的半成品目标,并将其存储在 earlySingletonObjects 缓存中。然后,持续创立该 Bean 所依靠的其他 Bean。当一切的 Bean 都被创立完结后,会调用 SmartInstantiationAwareBeanPostProcessor 的 postProcessAfterInitialization() 办法,将一切标记为“当时正在创立的 Bean”的半成品目标转化为完好的 Bean 目标,并存储在 singletonObjects 缓存中。
-
在转化半成品目标为完好的 Bean 目标的进程中,Spring 会经过 BeanWrapperImpl 中的 setPropertyValue() 办法为该 Bean 的特点赋值。在对特点进行赋值的进程中,假如存在依靠联系,会调用 getBean() 办法创立依靠的 Bean。假如创立依靠的 Bean 时发现循环依靠的问题,会先从 singletonFactories 缓存中获取该 Bean 的工厂办法,并将其存储在 singletonFactories 缓存中。然后,持续创立该 Bean 所依靠的其他 Bean。当一切的 Bean 都被创立完结后,会遍历 singletonFactories 缓存中的一切工厂办法,调用其 getObject() 办法,将其转换为完好的 Bean 目标,并存储在 singletonObjects 缓存中。
经过源码剖析,咱们能够看出,Spring 处理循环依靠问题的中心是经过三级缓存和 BeanPostProcessor 完结的。
- 在创立 Bean 的进程中,假如呈现循环依靠的问题,会先创立 Bean 的半成品目标,并将其存储在 earlySingletonObjects 缓存中。
- 持续创立该 Bean 所依靠的其他 Bean。当一切的 Bean 都被创立完结后,会调用 BeanPostProcessor 的 postProcessAfterInitialization() 办法,将一切标记为“当时正在创立的 Bean”的半成品目标转化为完好的 Bean 目标,并存储在 singletonObjects 缓存中。
- 在特点赋值的进程中,假如发现循环依靠的问题,会先将该 Bean 的工厂办法存储在 singletonFactories 缓存中,以便在创立其他 Bean 时运用。当一切的 Bean 都被创立完结后,会遍历 singletonFactories 缓存中的一切工厂办法,调用其 getObject() 办法,将其转换为完好的 Bean 目标,并存储在 singletonObjects 缓存中。