1.背景说明
● 循环依赖是什么?
有一个Bean为AService
,另一个Bean为BService
。
AService
里面引用了属性BService
,BService
里面又引用了属性AService
,这样在加载Bean的时候,造成对彼此的循环依赖。
这样会导致Bean无法加载。
● 解决循环依赖的思路
说明:这里只解决单例bean的循环依赖问题。
核心思路:只需要增加一个 缓存 来存放 原始对象 即可,Spring解决此类循环依赖主要是靠三级缓存在【属性注入】阶段产生作用。
具体做法:在创建 AService
时,实例化后将 原始对象 存放到缓存中(提早暴露),然后依赖注入时发现需要 BService
,便会去创建 BService
,实例化后同样将 原始对象 存放到缓存中,然后依赖注入时发现需要 AService
便会从缓存中取出并注入,这样 BService
就完成了创建,随后 AService
也就能完成属性注入,最后也完成创建。这样就打破了环形调用,避免循环依赖问题。
2.三级缓存
● 一级缓存:SingletonObjects,缓存的是已经实例化、属性注入、初始化后的bean对象。
● 二级缓存:earlySingletonObjects,缓存的是实例化后,但未属性注入、初始化的Bean对象,用于提前暴露Bean
● 三级缓存:singletonFactories,缓存的是一个ObjectFactory,主要作用是生成原始对象进行AOP操作后的代理对象
3.源码分析
3.1 入口
从 AbstractApplicationContext.java
的 这个方法进去:
// 初始化 非懒加载的单例 bean
beanFactory.preInstantiateSingletons()
;
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
进入到 DefaultListableBeanFactory的preInstantiateSingletons方法
:
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(
(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
getBean(beanName);
}
}
}
3.2 getBean方法
DefaultListableBeanFactory中,进入getBean
这里获取的是这种类型的bean:
(1)非抽象的BeanDefinition
(2)单例的BeanDefinition
(3)非懒加载的BeanDefinition
else {
getBean(beanName);
}
3.3 doGetBean方法
实际干活的是 AbstractBeanFactory中的 doGetBean
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException
如果不存在已有的单例对象,且beanDefination是单例,那么进入 createBean方法
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
3.4 createBean方法
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException
createBean方法 返回一个Object类型
核心逻辑:
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
3.5 doCreateBean方法
3.5.1 实例化对象
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException
● 首先,实例化一个对象:
instanceWrapper = createBeanInstance(beanName, mbd, args)
3.5.2 加入三级缓存
接着,把创建对象的lambda表达式放到三级缓存。
这里是为了避免后期的循环依赖,所以在bean初始化完成前将创建实例的ObjectFactory加入工厂。
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
说明:
(1)getEarlyBeanReference(beanName, mbd, bean) 方法 会返回遍历工厂内的所有后置处理器,去处理原始的bean,并返回最终经过层层包装后的 代理对象
(2)这里并不会马上执行getEarlyBeanReference方法,有循环依赖的时候才执行。
3.5.3 属性赋值
场景:AService 中要注入属性BService
进入populateBean方法
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)
核心方法:
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
applyPropertyValues中的核心方法:
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
点进去:
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}
核心方法:resolveReference(argName, ref),点进去:
else {
String resolvedName;
if (beanType != null) {
NamedBeanHolder<?> namedBean =
this.beanFactory.resolveNamedBean(beanType);
bean = namedBean.getBeanInstance();
resolvedName = namedBean.getBeanName();
} else {
resolvedName = String.valueOf(doEvaluate(ref.getBeanName()));
bean = this.beanFactory.getBean(resolvedName);
}
this.beanFactory.registerDependentBean(resolvedName, this.beanName);
}
注意:这里开始无限套娃之旅,即:
(1)这里有一句:bean = this.beanFactory.getBean(resolvedName),在该场景下,作用是获取属性Bservice。
(2)然后 B在创建过程中,最终又会执行这句bean = this.beanFactory.getBean(resolvedName),去获取属性Aservice
3.6 循环依赖的闭环
● 闭环形成的关键点
当 Aservice --> Bservice --> Aservice 这个过程,进行到第二次 获取 Aservice 的时候,会通过 getSingleton 方法
获取Aservice,即:
可以看到在第三级缓存中调用了 singletonFactories.get(beanName)
。
按照上文所说,会触发执行有 AOP 操作,返回代理对象。如果没有AOP操作的话,返回原始对象。
此外,还会把Aservice上移到二级缓存中、并删除三级缓存的数据。
● Bservice创建完成
如此一来,容器在创建Bservice的时候,就可以拿到 Aservice,完成BService的创建,并将Bservice加入一级缓存。
● Aservice创建完成
当Bservice创建完成后,就可以在Aservice中完成属性Bservice的注入,从而完成AService的创建,将Aservice加入一级缓存。
4.总结
梳理整个过程如下:
● 首先会获取 AService
对应的 Bean 对象。
先是调用 doGetBean()
中的第一个 getSingleton(beanName)
判断是否有该 Bean 的实例,有就直接返回了。(显然这里没有)
然后调用 doGetBean()
中的第二个 getSingleton()
方法来执行 doCreateBean()
方法。
先进行实例化操作(也就是利用构造函数实例化),此时实例化后生成的是原始对象。
将原始对象通过 lambda表达式 进行封装成 ObjectFactory
对象,通过 addSingletonFactory
加入三级缓存中。
然后再进行属性注入,此时发现需要注入 BService
的 Bean,会通过 doGetBean()
去获取 BService
对应的 Bean。
● 获取BService
对应的 Bean。
同样调用 doGetBean()
中的第一个 getSingleton(beanName)
判断是否有该 Bean 的实例,显然这里也是不会有 BService
的 Bean 的。
然后只能调用 doGetBean()
中的第二个 getSingleton()
方法来执行 doCreateBean()
方法来创建一个 BService
的 Bean。
同样地先进行实例化操作,生成原始对象后封装成 ObjectFactory
对象放入三级缓存中。
然后进行属性注入,此时发现需要注入 AService
的 Bean。
● 第二次获取AService
对应的 Bean
此时调用调用 doGetBean()
中的第一个 getSingleton(beanName)
查找是否有 AService
的 Bean。此时会触发三级缓存,也就是调用 singletonFactories.get(beanName)
。
因为三级缓存中有 AService
的原始对象封装的 ObjectFactory
对象,所以可以获取到的代理对象或原始对象,并且上移到二级缓存中,提前暴露给 BService
调用。
所以 BService
可以完成属性注入,然后进行初始化后,将 Bean 放入一级缓存,这样 AService
也可以完成创建。