文章目录
一、三级缓存解决循环依赖流程
假设要获取BeanA(BeanA依赖注入了BeanB,BeanB中依赖注入了BeanA)
1.获取Bean入口
- getBean()进入,其中的doGetBean中调用getSingleton(name,boolean);
2.getSingleton获取Bean
- 各级缓存中获取为空说明Bean还没创建,后面继续getSingleton(String beanName, ObjectFactory<?> singletonFactory),singletonFactory是一个lambada表达式。
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object bean;
//省略无关代码
// 首先通过这个方法获取,若获取为空,下面调用另一个getSingleton获取
Object sharedInstance = getSingleton(beanName);
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//省略无关代码
return (T) bean;
3.之前Bean没有被创建过,获取Bean为空,开始createBean
- 上述的getSingletonBean进去,里面获取singletonObject = singletonFactory.getObject();【注释A】
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
singletonFactory是一个lambada表达式,因此执行其中的createBean方法获取结果。
createBean中又会调用doCreateBean()。
doCreateBean()就是一个创建Bean的过程。
4.doCreateBean中进行实例化并将Bean加入三级缓存
- doCreateBean中首先实例化Bean,见createBeanInstance。
- 实例化完成后会把实例化对象BeanA加入三级缓存addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
- 注意addSingletonFactory里面没有获取lambada表达式的返回值,所以不会执行getEarlyBeanReference,只是将其设置为三级缓存的value
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
//value是lambada表达式传过来的一个工厂
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
由此可见,三级缓存保存的就是这个工厂,以lambada形式的表达式传过来
- 所以当需要从三级缓存中获取对象时,从工厂中获取,如果需要AOP代理,那么其中也可完成。
代理是在getEarlyBeanReference的exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);中完成的。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//Bean生命周期中的实例化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//加入三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
//Bean生命周期中的属性赋值
populateBean(beanName, mbd, instanceWrapper);
//Bean生命周期中的初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
return exposedObject;
}
5.属性注入与循环依赖产生
- 加入三级缓存后BeanA继续进行(见上述代码中的属性注入和初始化)
(1)属性赋值populateBean(beanName, mbd, instanceWrapper);,
(2)初始化initializeBean(beanName, exposedObject, mbd); - 循环依赖就在属性赋值populateBean中产生,属性赋值中有可能要Autowired其他的BeanB
//截取populateBean中的部分代码
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
//可以继续点击进去查看,里面会调用getBean()方法,而getBean其实又调用了doGetBean,看,是不是回到了开头。
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
- 此时Autowired其他的BeanB,doGetBean中调用getSingleton(name,boolean),
- 若BeanB还未创建
- 那么会按上述流程继续创建BeanB;
- 创建BeanB执行到属性注入阶段BeanB中要依赖注入BeanA;此时BeanA已经在三级缓存中,因此BeanB可以从三级缓存中获取到BeanA,获取到后将BeanA加入二级缓存并移出三级缓存。
- BeanB执行完实例化,属性注入,初始化返回真正的BeanB,从而BeanA中可以注入BeanB.。
- 若BeanB已经存在三级缓存当中
- 那么getSingleton中会将其加入二级缓存,并从三级缓存中移除;
这样子其实BeanB就成功注入BeanA了
- 那么getSingleton中会将其加入二级缓存,并从三级缓存中移除;
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//【注释B】通过getEarlyBeanReference获取Bean,如果需要代理则返回代理对象
singletonObject = singletonFactory.getObject();
//加入二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
//移出三级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
6.初始化完成Bean,加入一级缓存
- 成功Autowired注入属性BeanB,属性注入完成,初始化完成,doCreateBean返回真正的BeanA实例;(doCreateBean–返回给–>createBean–返回给–>getSingleton)
- getSingleton里面的singletonFactory.getObject()获取到BeanA,最后会把BeanA加入一级缓存
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
7.方法调用整理
- getBean()->doGetBean()->getSingleton()->createBean()->doCreateBean()[这其中进行实例化(加入三级缓存),属性注入,初始化,获取到真正的Bean]
- 其实这其中也是Bean生命周期的一个大概流程:
实例化createBeanInstance()–>属性注入populateBean()–>初始化initializeBean()
当然,Bean的销毁不在这其中
备注
注意上述有两个注释,【注释A】和【注释B】,这两个地方虽然都是singletonFactory.getObject(),但是singletonFactory是不一样的lambada表达式;
【注释A】是:
() -> {
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;
}
}
【注释B】是:
() -> getEarlyBeanReference(beanName, mbd, bean)
二、总结
1.三级缓存的作用?
- 三级缓存中保存的是工厂,如果Bean进行了AOP代理,可以通过工厂获取到一个代理对象,而不是对象本身。
- 在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,
如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。
2.有了三级缓存工厂为什么还要二级缓存(二级缓存的作用)?
假设A中依赖B,B中依赖A,C中依赖A。A实例化后放入三级缓存,对于一个Bean不能多次从三级缓存拿:
- 因为三级缓存保存的是ObjectFactory对象,一个对象工厂,通过工厂获取的对象可能是不一样的(上文分析过三级缓存保存的是工厂,以一个lambada表达式传过来);
- 因此使用了二级缓存,B从三级缓存中获取A后就把A放入二级缓存,后面C就可以从二级缓存拿A了,这样能保证B和C拿到的对象A是一致的。
- 注意:此时的A加入二级缓存还只是半成品,因为之前三级缓存中获取到A,说明还没完成属性注入和初始化(如果已经初始化完成会从三级缓存和二级缓存中删除,加入一级缓存),还不是最终初始化完成的Bean.
- 当A完成属性注入,初始化完成后再放入一级缓存。
3.既然为了多个地方getBean()获取到同一个对象所以使用二级缓存,那为什么不直接在三级缓存中放入代理对象?
- 如果这样那么意味着不管有没有循环依赖所有的Bean在实例化后都要完成AOP代理,违背了Spring在结合AOP跟Bean的生命周期的设计。
- Spring的设计是在AbstractAutoProxyCreator.postProcessAfterInitialization完成AOP代理。
(可以看到该类中wrapIfNecessary这个方法有2个调用,一个是为了解决循环依赖的getEarlyBeanReference,一个就是Spring计划完成代理的postProcessAfterInitialization)
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
//代理的实现
return wrapIfNecessary(bean, beanName, cacheKey);
}
4.构造注入不能解决循环依赖
- 因为构造方法创建实例,每次都要new一个要构造的实例bean,
而A创建时,依赖B,就去创建B,B又依赖了A,继续构造A,如此循环下去 A(B) B(A) A(B)->…
5.原型(Prototype)多例场景是不支持循环依赖
- 因为“prototype”作用域的Bean,为每一个bean请求提供一个实例,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean,会抛出异常。
https://blog.csdn.net/W_317/article/details/108687587
6.三级缓存
- 一级缓存 : Map<String,Object> singletonObjects,单例池,用于保存实例化、注入、初始化完成的bean实例
- 二级缓存 : Map<String,Object> earlySingletonObjects,早期曝光对象,用于保存实例化完成的bean实例
- 三级缓存 : Map<String,ObjectFactory<?>> singletonFactories,早期曝光对象工厂,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。
参考文章:
https://mp.weixin.qq.com/s/fX4ze5YuLI5cqVaZhxq1tw
https://www.icode9.com/content-4-852133.html
https://javazhiyin.blog.csdn.net/article/details/107602783