简介
说明
本文用实例来介绍@Autowired解决循环依赖的原理。@Autowired是通过三级缓存来解决循环依赖的。
除了@Autoired,还有其他方案来解决循环依赖的
概述
@Autowired进行属性注入可以解决循环依赖。因为按Java的类加载流程来说,是先实例化,后注入属性的。Spring中记录了正在创建中的bean(已经实例化但还没初始化完毕的bean),所以可以在之后注入属性时,从记录的bean中取依赖的对象。
相对而言,单纯使用构造器注入就无法解决循环依赖。因为,在构造时就需要传入依赖的对象,导致无法实例化。(注意:构造器注入可以使用@Lazy解决循环依赖,在实例化时,传入代理对象,真正使用时才会生成真正的对象)
实例
代码
package com.example.tmp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class A {
@Autowired
private B b;
private String name = "Tony";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTest() {
return b.getAge().toString() + name;
}
}
package com.example.tmp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class B {
@Autowired
private A a;
private Integer age = 20;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
package com.example.controller;
import com.example.tmp.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
private A a;
@GetMapping("/test1")
public String test1() {
return a.getTest();
}
}
测试
1.启动不报错。
2.postman访问:http://localhost:8080/test1
后端结果:不报错
postman结果: 20Tony
打断点分析
简介
由上边两篇博客,可以定位到实例化的代码位置:
AbstractAutowireCapableBeanFactory#doCreateBean(beanName, mbdToUse, args)
1.总入口:populateBean
总结
经过下边的定位,可确定,核心代码在:AbstractAutowireCapableBeanFactory#populateBean(beanName, mbd, instanceWrapper);
doGetBean 中有两个 getSingleton 方法会先后执行。第一个是尝试从缓存中获取,若缓存中没有 A,则执行第二个,通过工厂获得。
public Object getSingleton(String beanName) 。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)。
第一次通过getSingleton(String beanName)获取bean的时候,缓存中是没有的,于是走到getSingleton(String beanName, ObjectFactory<?> singletonFactory)
流程概述
getBean(beanName) //AbstractBeanFactory抽象类(实现的BeanFactory接口)
doGetBean(name, null, null, false) //AbstractBeanFactory抽象类。自己的方法。
// 先从单例的缓存中取,取不到则走下一个getSingleton
getSingleton(beanName);
getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}}
)
createBean(beanName, mbd, args) //AbstractAutowireCapableBeanFactory抽象类(实现的AbstractBeanFactory)
doCreateBean(beanName, mbdToUse, args)//AbstractAutowireCapableBeanFactory抽象类。以下为此类方法
createBeanInstance(beanName, mbd, args); //创建实例
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName) //post-processors修改bean的定义
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); //解决循环依赖
populateBean(beanName, mbd, instanceWrapper); //填充属性
断点调试
打条件断点(DefaultListableBeanFactory#doCreateBean(beanName, mbdToUse, args)
定位代码:实例化A时去实例化B并填充到A对象
在populateBean前后打断点。
结果:
- populateBean前(A的):A已经实例化,其字段b还是null。(图1)
- populateBean后(A的):没走到下一行,而是去实例化B去了,再次到了populateBean前。(图2)
- 此时,B已经实例化,其字段a为null
- populateBean后(B的):的对象里已经给字段a赋值了,但a的字段b仍然为null。(图3)
- populateBean后(A的):在第3步执行完之后,堆栈执行到A的populateBean后边。(图4)
- 此时,A对象里有B对象,B对象里边有A对象,一直循环
图1:
图2:
图3:
图4:
2.后置处理器注入属性
简介
经过上边的定位,可以确定,核心代码在:AbstractAutowireCapableBeanFactory#populateBean(beanName, mbd, instanceWrapper);
打条件断点:
总结(代码流程)
getBeanPostProcessors() // 其中有:AutowiredAnnotationBeanPostProcessor (图1)
ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
// AutowiredAnnotationBeanPostProcessor
postProcessProperties(PropertyValues pvs, Object bean, String beanName) (图2)
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
metadata.inject(bean, beanName, pvs);
inject(bean, beanName, pvs) //InjectionMetadata
element.inject(target, beanName, pvs); //InjectionMetadata (图3)
// AutowiredAnnotationBeanPostProcessor
// 第一次进来时,this.cached为false。去解决依赖(B对象)(图4)
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)
// 上边获得之后,使用反射,设置字段的值
field.set(bean, value);
图1:
图2:
图3:
图4:
3.解决依赖
总结
实例化B时,由于之前实例化过A,这次 getSingleton(String beanName) 中发生了不一样的事:能够获得 A 的缓存。
简介
从上一步可以看到,最终调用DefaultListableBeanFactory#resolveDependency,来解决 A => B 的依赖,需要去获取 B,仍然通过 getBean 获取,和之前说 getBean 获取 A 的过程类似,只是这次换成了 B。
调用栈如下:getBean=> doGetBean=> getSingleton,又是熟悉的步骤,但这次 getSingleton(String beanName) 中发生了不一样的事:能够获得 A 的缓存。
4.三级缓存
总结
可以在第3级缓存中获得到bean。
简介
在AbstractBeanFactory#doGetBean()上打两个条件断点:beanName.equals("a") || beanName.equals("b")
// 为什么不在getSingleton里边打断点?因为getSingleton除了获取bean之外,其他也有地方调用了,会影响我们本处的分析。
断点1:
断点2:
第1次到达断点1:Controller对象获取A对象,三级缓存中都没有
第1次到达断点2:缓存中都没有对象A,所以去创建
第2次到达断点1: 因为A类依赖了B类,所以获取B对象。但也是:三级缓存中都没有B对象
第2次到达断点2: 缓存中都没有对象B,所以去创建
第3次到达断点1: 因为B类依赖了A类,所以获取A对象。此时:第1级缓存中已经有A对象了
第4次到达断点1:A用@Component修饰,要扫描进来。此时缓存中已经有了
第5次到达断点1:B用@Component修饰,要扫描进来。此时缓存中已经有了
三级缓存
概述
三级缓存流程如下:
- 从一级缓存singletonObjects中获取。()
- 若一级缓存中获取不到,则从二级缓存earlySingletonObjects中获取
- 若二级缓存中获取不到,则从三级缓存singletonFactories中获取
- 通过beanName获得对应的ObjectFactory,若能够获取到,则:
- 调用ObjectFactory#getObject获得真正的bean
- 把此bean从三级缓存移到二级缓存。即:singletonFactories中移除,并放入earlySingletonObjects中。
DefaultSingletonBeanRegistry 里边的三级缓存对应的map,如下所示:
/** Cache of singleton objects: bean name to bean instance. */
// 缓存单例Bean。key:bean名字,value:bean实例
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of early singleton objects: bean name to bean instance. */
// 缓存半成品单例Bean。key:bean名字,value:bean实例(已实例化,但未注入属性和初始化)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Cache of singleton factories: bean name to ObjectFactory. */
// 缓存单例bean的工厂。key:bean名字,value:能够生成bean的工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
代码位置
从上边可以追踪到从缓存中获取bean的位置:DefaultSingletonBeanRegistry#getSingleton(String beanName, boolean allowEarlyReference)
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) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
bean何时被加入第3级缓存?
简介
在上边“4.三级缓存”中可以看到,第三级缓存(singletonFactories)里边放入了以bean名为键,ObjectFactory为值的项。那么,是何时放入的呢?
答:在AbstractAutowireCapableBeanFactory#doCreateBean中,populateBean之前放入的,方法为:addSingletonFactory
doCreateBean //AbstractAutowireCapableBeanFactory
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
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));
}// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
addSingletonFactory //DefaultSingletonBeanRegistry
// 放入以bean名为键,ObjectFactory为值的项
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
getEarlyBeanReference //AbstractAutowireCapableBeanFactory
// 返回 A 的引用。(虽然 A 还在创建,未完成。)
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
三级缓存流程图
流程图
总结
- 对于不同的bean,获取某个循环依赖的对象的位置是不同的。
- 对于第一次获取:缓存中没有,会去创建它,然后获取。
- 对于第二次获取:第3级缓存中有,会从第3级缓存中获取,然后将其转移到第2级缓存
getSingleton(String beanName, ObjectFactory<?> singletonFactory)
代码流程:
入口
getSingleton(String beanName, ObjectFactory<?> singletonFactory) //DefaultSingletonBeanRegistry
// DefaultSingletonBeanRegistry
singletonObject = singletonFactory.getObject();
addSingleton(beanName, singletonObject);addSingleton(String beanName, Object singletonObject) // DefaultSingletonBeanRegistry
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
为什么用三级缓存,而不是二级缓存
简介
在上边的分析中我们可以提出两个问题:
- 二级缓存好像没有用到?那么它什么时候会用到?
- 为什么第三级缓存要用一个工厂,删除第三级缓存,只用第一二级不可以吗?
可以去掉第二级缓存吗?
简介
不可以去掉第二级缓存。
详解
假如有这种情况:a实例同时依赖于b和c,b和c都依赖于a。
a实例化时,先提前暴露objectFactorya到三级缓存,调用getBean(b)依赖注入b实例。b实例化之后,提前暴露objectFactoryb到三级缓存,调用getBean(a)依赖注入a实例,由于提前暴露了objectFactorya,此时可以从三级缓存中获取到a实例, b实例完成了依赖注入,升级为一级缓存。a实例化再getBean(c)依赖注入c实例,c实例化之后,提前暴露objectFactoryc到三级缓存,调用getBean(a)依赖注入a实例,由于提前暴露了objectFactorya,此时可以从三级缓存中获取到a实例。注意这里又要从三级缓存中获取a实例,我们知道三级缓存中的实例是通过调用singletonFactory.getObject()方法获取的,返回结果每次都可能不一样。如果不用二级缓存,这里会有问题,两次获取的a实例不一样。
可以去掉第三级缓存吗?
结论
不可以去掉第三级缓存。
原因
Spring 的设计原则是在 IOC 结束之后再AOP( bean 实例化、属性设置、初始化之后再通过进行AOP(生成代理对象))。即:AOP的实现需要与bean的生命周期的创建分离。
- 为了解决循环依赖但又尽量不打破这个设计原则的情况下,使用了第三级缓存(key:bean名字,value:ObjectFactory)。
- 前边分析过,它是将一个函数式接口作为ObjectFactory,相当于延迟初始化。AOP中发生循环依赖时,通过调用Object的getObject()方法获取到三级缓存中的对象。
- 如果去掉第三级缓存,将AOP的代理工作放到第二级缓存,这样的话,bean在创建过程中就先生成代理对象再初始化和其他工作,与Spring的AOP的设计原则相违背。
详解
上边:“bean何时被加入第3级缓存?” 我们可以得到,第三级缓存里存放的value是:AbstractAutowireCapableBeanFactory#getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean)。
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; }
可以看到实际调用SmartInstantiationAwareBeanPostProcessor接口的getEarlyBeanReference(Object bean, String beanName)方法,此接口有以下实现类:
InstantiationAwareBeanPostProcessorAdapter
@Override public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { return bean; }
直接返回bean,这里看起来第三级缓存没必要存在。但是,看另一个实现类:
AbstractAutoProxyCreator
@Override public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey); }
这里把对象放入第二级缓存。 曾分析过AOP:将横切逻辑织入目标Bean:AbstractAutoProxyCreator#postProcessAfterInitialization方法,在Bean实例化之后被调用。
来看postProcessAfterInitialization
@Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
把key对应的项从二级缓存中移除,如果原来二级缓存中就是这个bean,则返回此bean,否则,返回代理类。