1、什么是循环依赖
所谓循环依赖是指,在A注入了B,在B中注入了A。初始化A时需要先初始化B,初始化B又需要初始化A,从而出现的类似死锁的现象。
2、spring 如何解决
2.1、循环依赖示例
@Component
public class A {
@Autowired
public B b;
}
@Service("b")
public class B {
@Autowired
public A a;
}
在springIOC初始化过程中,我们了解到非懒加载的单例Bean都是在AbstractApplicationContext.refresh()
方法中调用finishBeanFactoryInitialization()
方法,最终通过AbstractBeanFactory.doGetBean()
方法进行初始化的。那么spring如何解决循环依赖也必将在这个方法下面[spring 5.2版本源码]。
2.2、spring_bean生成时序图
在阅读源码前,先看看bean生成的时序图有助于理解源码。
- spring尝试通过
ApplicationContext.getBean()
方法获取A对象的实例,由于spring容器中还没有A对象[getSingleton(A) = null
],因此spring会创建A对象【spring创建一个bean分为三步:1. 实例化bean、 2. 给bean注入属性、3. 初始化bean】。并将A对象的半成品【未注入属性,未初始化】保存在三级缓存中[addSingletonFactory(A)
]。 - 然后为A对象注入属性B,通过
getBean(B)
从spring容器中尝试获取B对象,由于spring容器还没有B对象,会创建B对象。- 创建 B 的半成品对象,并保存在三级缓存中[
addSingletonFactory(B)
]。
- 创建 B 的半成品对象,并保存在三级缓存中[
> - 然后为B对象注入属性A,通过getBean(A)从三级缓存中获取A的半成品对象的引用,将A从三级缓存移入二级缓存。并将它做为属性注入B对象
> - 初始化B对象后,返回B对象。将B对象保存到一级缓存中
- 最后将返回的B对象做为属性注入A对象,初始化A,并将A对象保存在一级缓存中。
2.3、源码解读
非懒加载的单例Spring_Bean最终都通过AbstractBeanFactory.doGetBean()
进行初始化的。我们的源码分析也从这一个方法开始.
忽略其中与本次关联不大的代码
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object bean;
// 检查是否目标bean是否已经注册过.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
}else {
// 单例
if (mbd.isSingleton()) {
// 将获取到的bean 加入一级缓存
sharedInstance = getSingleton(beanName, () -> {
try {
// 创建 bean对象
return createBean(beanName, mbd, args);
}catch (BeansException ex) {
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
return (T) bean;
}
其中值得关注的方法有三个:
- getSingleton(beanName);
- getSingleton(beanName,ObjectFactory);
- createBean(beanName, mbd, args);
接下来我们详细看一下这三个方法源码。2.3.1、getSingleton(bean)
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 查询一级缓存 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 查询二级缓存 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { synchronized (this.singletonObjects) { // Consistent creation of early reference within full singleton lock singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { // 查询三级缓存 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); // 如果查询到将查询到的数据写入二级缓存,然后从三级缓存中移除 if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } } } return singletonObject; }
- 该方法分别从一级、二级、三级缓存中尝试获取bean,如果能获取到直接返回该对象,否则返回null。
isSingletonCurrentlyInCreation
方法用于判断目标bean是否处于正在创建阶段,如果是返回true。- 如果入参
allowEarlyReference
为true,才会尝试查询三级缓存。且在三级缓存中查询到之后会将bean对象保存在二级缓存。2.3.2、createBean(beanName, mbd, args)
在getSingleton(beanName,ObjectFactory)
方法中的第二个参数由createBean(beanName, mbd, args)
提供,所以我们先分析createBean方法。
createBean(beanName, mbd, args)委托给同类的doCreateBean(beanName, mbdToUse, args)
方法实现。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// BeanWrapper 是bean的包装类 它提供了 getPropertyDescriptors 方法 获取bean的属性
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// 创建bean实例的包装类
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// earlySingletonExposure 1. 是单例。2. bean允许循环依赖。3. 该单例bean正在被创建
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");
}
// 加入三级缓存 - getEarlyBeanReference 方法 默认返回入参中的bean对象
// 此时的bean未注入属性,也就是@Autowried等注解还没有被解析
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
// 注入属性,@Autowired 等在此处解析
populateBean(beanName, mbd, instanceWrapper);
// bean初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {}
// 去掉部分相对不重要代码
return exposedObject;
}
spring_bean生成的三大步骤:
- 实例化:createBeanInstance(beanName, mbd, args)
- 属性注入:populateBean(beanName, mbd, instanceWrapper);
- 初始化:initializeBean(beanName, exposedObject, mbd);
在实例化和属性注入之间有一行关键性代码:addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
getEarlyBeanReference(beanName, mbd, bean)
方法最终会委托AbstractAutoProxyCreator.wrapIfNecessary()
实现动态代理返回一个bean对象的代理类。
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
然后通过addSingletonFactory()将该代理类加入到三级缓存。
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);
}
}
}
populateBean(beanName, mbd, instanceWrapper)
该方法会解析bean类中需要注入的属性,如果需要注入的是一个bean对象,spring通过反射获取对应的bean,最终还是调用getBean()
方法。
由于本次例子中是通过@Autowired
注解进行属性注入,@Autowired其实是通过AutowiredAnnotationBeanPostProcessor
后置处理器进行属性注入的。
我们来看看属性是如何注入
// 获取需要注入的 被@Autowired修饰的属性
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 从metadata中获取需要注入的bean_name,然后最终通过getBean()方法进行获取
metadata.inject(bean, beanName, pvs);
}
最终调用descriptor.resolveCandidate(autowiredBeanName, type, this);获取需要注入的bean
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
throws BeansException {
return beanFactory.getBean(beanName);
}
这边调用链路有点长,此处就不做赘述。有兴趣的可以根据以下链路自行查看
- AbstractAutowireCapableBeanFactory.populateBean()
- AutowiredAnnotationBeanPostProcessor.postProcessProperties()
- AutowiredAnnotationBeanPostProcessor.inject()
- DefaultListableBeanFactory.resolveDependency()
- DefaultListableBeanFactory.doResolveDependency()
- DependencyDescriptor.resolveCandidate()
总结
查看完以上源码,我们再次回顾一下本次的面试题《spring 怎么解决循环依赖?》
spring容器中存在三级缓存【分别是singletonObjects、earlySingletonObjects、singletonFactories】,spring在实例化bean之后,会将bean对象的引用添加到三级缓存中,在循环依赖发生时,spring会将正在初始化过程中的不完全bean的引用先作为属性注入。最终将bean对象初始化后添加到一级缓存【singletonObjects】中。
假设存在A 和 B 存在循环依赖,此时先将A注册到spring容器中。
- 先尝试在spring的三级缓存中获取bean对象,如果获取不到,创建一个新的bean A
- 先实例化A,然后将不完全的A对象的引用,保存到三级缓存中。
- 再对A进行属性B的注入,发现B对象还没有再spring容器中,创建B
- 实例化B,然后将不完全的B对象的引用,保存在三级缓存中。
- 然后对B进行属性A的注入,通过查询发现A对象存在于三级缓存中,将A对象保存到二级缓存中。然后注入B中。【B对象中注入的是A对象的引用】
- 初始化B之后,此时对象B是一个完整的bean,将它保存在一级缓存中。
- B创建成功后,返回一个B对象的引用。并将其注入A。
- A进行初始化【A初始化完成后,A也是一个完整的bean,那么B中A属性引用对象也完整了】,然后保存到一级缓存中。
虽然存在循环依赖,但是在构造器注入的情况下,循环依赖仍然会报错。
根据源码我们得知setter注入和注解注入,是在populateBean方法上进行的递归(getBean),此时三级缓存已经保存,所以循环依赖不会出错。
但是构造器注入的递归操作发生在createBeanInstance(beanName, mbd, args)这个方法中。此时三级缓存还没有保存,在缓存中获取失败时,又会重新create新的bean。这也就出现了循环依赖。
有兴趣的小伙伴可以跟一下源码。
代码链路:
- createBeanInstance(beanName, mbd, args)
- AbstractAutowireCapableBeanFactory.autowireConstructor()
- ConstructorResolver.autowireConstructor()
- ConstructorResolver.resolveConstructorArguments()
- BeanDefinitionValueResolver.resolveValueIfNecessary();
- BeanDefinitionValueResolver.resolveReference()
- 最终在这个方法中调用了getBean()方法