这里写自定义目录标题
- 循环依赖主要场景:
- 构造方法循环依赖(spring无法解决)
- Spring解决循环依赖的限制条件
- Spring是怎么解决循环依赖的?
- **三级缓存解决方案**
- **一张图描述整个过程**
开局一张图,今天带你们搞清楚Spring循环依赖。
首先看一个启动异常
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| orderService defined in file [./target/classes/cn/xxx/spring/OrderService.class]
↑ ↓
| userService defined in file [./target/classes/cn/xxx/spring/UserService.class]
└─────┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
应该由不少同学遇到过这种情况,很荣幸,遇到循环依赖了。SpringBoot2.6.x默认禁用循环依赖,所以需要添加如下配置解决。
spring:
main:
allow-circular-references: true
可能你的问题到此就解决了,那么spring是怎么解决循环依赖的呢?我来解释一下。
循环依赖主要场景:
构造方法循环依赖(spring无法解决)
假设有两个类,A和B,它们相互依赖。A类的构造方法需要一个B类的实例,而B类的构造方法需要一个A类的实例。这是一个典型的循环依赖问题。
下面是示例代码:
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
public class B {
private A a;
public B(A a) {
this.a = a;
}
}
在这个例子中,当Spring尝试创建A和B时,它们之间的循环依赖将导致创建失败。这种构造方法的循环依赖解决起来有一定复杂度,默认spring是无法直接解决的。
Spring解决循环依赖的限制条件
(1)Spring只能解决单例Bean的循环依赖问题。如果两个原型Bean相互引用,则Spring无法解决它们。
(2)非代理对象,如果两个Bean都需要使用代理对象,则Spring也无法解决它们。
(3)满足以上两个条件的主bean通过属性或者setter方法注入所依赖的bean,而不是通过构造函数注入。
这样的循环依赖Spring是可以解决的,解决方式就像刚开始的配置那样简单。
虽然解决问题很简单,那么其中的原理大家可能也希望了解一下,毕竟这也算是面试八股必背题。那么今天就给大家讲清楚,spring怎么解决循环依赖。
Spring是怎么解决循环依赖的?
一句话描述,为了解决这个问题,Spring使用了一个名为"临时Bean引用"的技术。它的工作原理是,当一个Bean被创建时,Spring将其放在一个早期暴露的Bean工厂中,然后创建另一个Bean。当创建另一个Bean时,Spring会将第一个Bean的实例注入到第二个Bean的setter方法中。然后,当第一个Bean被完全创建时,Spring将把它的实例注入到第二个Bean的setter方法中。也就是我们可能听说过的spring三级缓存,下面详细介绍一下三级缓存解决方案。
三级缓存解决方案
spring内部有三级缓存:
成品:一级缓存 singletonObjects ,用于保存实例化、注入、初始化完成的bean实例 。
半成品:二级缓存 earlySingletonObjects ,用于保存实例化完成的bean实例。
原材料工厂: 三级缓存 singletonFactories ,用于保存bean的创建工厂,以便于后面扩展有机会
创建代理对象。
三级缓存是singletonFactories ,但是是不完整的Bean的工厂Factory,是当一个Bean在new之后 (没有属性填充、初始化),就put进去。所以,是原材料工厂 二级缓存是对三级缓存的过渡性处理,只要通过 getObject() 方法从三级缓存的BeanFactory中 取出Bean一次,原材料就变成变成品,就put到二级缓存 , 所以,二级缓存里边的bean,都是半 成品 一级缓存里面是完整的Bean,是当一个Bean完全创建后(完成属性填充、彻底的完成初始化)才put 进去, 所以,是成品
一张图描述整个过程
那么说到这里,有可能会有人觉得二级缓存不重要如果没有二级缓存行不行???
假设不用第二级缓存,TestService1注入到TestService3的流程如图:
TestService1注入到TestService3又需要从第三级缓存中获取实例,而第三级缓存里保存的并非真正的 实例对象,而是 ObjectFactory 对象。说白了,**两次从三级缓存中获取都是 ObjectFactory 对象,而通过它创建的实例对象每次可能都不一样的。**为了解决这个问题,spring引入的第二级缓存。 前一个图其实TestService1对象的实例已经被添加到第二级缓存中了,而在TestService1注入到TestService3时,只用从第二级缓存中获取该对象即可。
下面看一下Spring源码
在AbstractBeanFactory 的 doGetBean() 方法中,我们根据BeanName去获取Singleton Bean的时 候,会先从缓存获取。
/**
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
//通过一级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);
//一级缓存获取不到,且当前bean在创建
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 如果此时bean正在加载(bean 在 earlySingletonObjects 中),则直接将singletonObject 返回。
singletonObject = this.earlySingletonObjects.get(beanName);//从 二级缓存 earlySingletonObjects 中获取
// allowEarlyReference = true 才会允许循环依赖
if (singletonObject == null && allowEarlyReference) {
// 如果单例缓存中不存在该bean,则加锁进行接下来的处理
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 从 二级缓存 earlySingletonObjects 中获取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 当某些方法需要提前初始化的时候则会调用addSingletonFactory
// 将对应的ObjectFactory初始化策略存储在singletonFactories中
// 从 三级缓存 singletonFactories 中获取对应的 ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//从单例工厂中获取bean,调用预先设定的getObject方法
singletonObject = singletonFactory.getObject();
// 添加到二级缓存earlySingletonObjects 和 singletonFactories互斥
this.earlySingletonObjects.put(beanName, singletonObject);
//删除三级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
getSingleton()的查找bean的次序比较清晰:
1、从一级缓存singletonObjects (成品仓库)中获取单例Bean。
2、一级缓存获取不到,则从二级缓存earlySingletonObjects (半成品仓库)中获取单例Bean。
3、二级缓存获取不到,则从三级缓存singletonFactories(原材料工厂 仓库)中获取单例BeanFactory原材料工厂。
4、如果从三级缓存中拿到了BeanFactory,则通过getObject()生产一个最原始的bean,可以理解为
最原始的bean实例(原材料),没有进行属性填充,也没有完成初始化,由于此处是提取bean,所以bean原材料已经被使用了一次,顺手把Bean(半成品)存入二级缓存中,并把该Bean的(原材料工厂)从三级缓存中删除。
三级缓存里边的原材料工厂(singletonFactory)在哪里设置呢
// Eagerly cache singletons to be able to resolve circular references
//急切地缓存单例以便能够解析循环引用
// even when triggered by lifecycle interfaces like BeanFactoryAware.
//甚至由 BeanFactoryAware 等生命周期接口触发
//单例模式
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// 当前单例 bean 是否正在被创建
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
这就是put三级缓存 singletonFactories 的地方,满足以下3个条件时,把bean加入三级缓存中: 1、单例 2、允许循环依赖
3、当前单例Bean正在创建
addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) 方法,将原始 的bean匿名工厂 (原材料工厂)加入到三级缓存,代码如下:
/**
* Add the given singleton factory for building the specified singleton
* if necessary.
* 如果有必要则添加给定的单例工厂以构建指定的单例。
* <p>To be called for eager registration of singletons, e.g. to be able to
* resolve circular references.
* @param beanName the name of the bean
* @param singletonFactory the factory for the singleton object
*/
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);
}
}
}
singletonFactories 这个三级缓存才是解决 Spring Bean 循环依赖的关键。 ***注意:***这段代码发生在 createBeanInstance(…) 方法之后,也就是说这个 bean 其实已经被创建 出来了,但是它还没有完善(没有进行属性填充和初始化)
下面看下doCreateBean过程:
/**
* Actually create the specified bean. Pre-creation processing has already happened
* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
* <p>Differentiates between default bean instantiation, use of a
* factory method, and autowiring a constructor.
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @param args explicit arguments to use for constructor or factory method invocation
* @return a new instance of the bean
* @throws BeanCreationException if the bean could not be created
* @see #instantiateBean
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
*/
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
// 如果定义的是单例模式,就先从缓存中清除
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// 如果 instanceWrapper 为空,那就 创建对应的beanInstance,具体方法在下面小节分析
if (instanceWrapper == null) {
//这个 bean 其实已经被创建出来了
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
// 将 解析类型 设置 为 beanType
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
// 使用后置处理器 对其进行处理
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// 这里主要是 MergedBeanDefinitionPostProcessor
//对@Autowire,@Value等这些注解进行处理, 相关的可以
// 参考AutowiredAnnotationBeanPostProcessor 相关逻辑
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
/** 是否需要提前曝光: 单例& 允许循环依赖 & 当前bean正在创建中, 检查循环依赖
* 这里主要是调用 方法addSingletonFactory ,往缓存singletonFactories里面 放入一个 ObjectFactory
* 当其他的bean 对该bean 有依赖时,可以提前获取到
* getEarlyBeanReference方法就是获取一个引用, 里面主要是
* 调用了 SmartInstantiationAwareBeanPostProcessor,
* 的 getEarlyBeanReference 方法,以便解决循环依赖问题, 这里 一般都是bean 本身,
* 在 AOP时 是代理
**/
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");
}
// 为避免后期循环依赖,可以在 bean 初始化完成前将创建实例的 objectFactory加入缓存
// 对bean 再一次依赖引用,主要应用SmartInstantiationAwareBeanPostProcessor
// 其中我们熟悉的AOP就是在这里将advice 动态织入,若没有直接返回bean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
// 下面就是初始化实例了
Object exposedObject = bean;
try {
// 对bean进行填充,将各个属性值注入,其中可能存在依赖于其他bean的属性,会递归初始化
populateBean(beanName, mbd, instanceWrapper);
//进一步初始化Bean
//注入 Aware 相关的对象
// 调用 后置处理器 BeanPostProcessor 里面的postProcessBeforeInitialization方法
// 调用 initialzingBean,调用实现的 afterPropertiesSet()
// 调用 init-mothod,调用相应的init方法
// 调用 后置处理器 BeanPostProcessor 里面的调用实现的postProcessAfterInitialization方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
//earlySingletonReference 只有在检测到有循环依赖的情况下才会不为空
if (earlySingletonReference != null) {
//如果exposedObject 没有在初始化方法中被改变,也就是没有被增强
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
// 检查依赖
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
/**
* 因为 bean 创建后其所依赖的bean一定是已经创建,
* actualDependentBeans 不为空则表示 当前bean 创建后其依赖的bean 却没有全部创建,
* 也就是说存在依赖
*/
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
// 注册到 disposableBeans 里面,以便在销毁bean 的时候 可以运行指定的相关业务
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
设置到三级缓存之后,对于其他依赖它的对象而言已经足够了(已经有内存地址了,可以根据对象引用定位到堆中对象),能够被认出来了。 addSingletonFactory方法的第二参数,通过匿名对象的方式,创建了一个 ObjectFactory 工厂,这个 工厂只有一个方法,源码如下
@FunctionalInterface
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}
一级缓存在哪里设置呢?
到这里我们发现三级缓存 singletonFactories 和 二级缓存 earlySingletonObjects 中的值都有出处了,
那一级缓存在哪里设置的呢?
在类 DefaultSingletonBeanRegistry 中,可以发现这个 addSingleton(String beanName, Object
singletonObject) 方法,代码如下:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
......
@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
Assert.notNull(beanName, "Bean name must not be null");
Assert.notNull(singletonObject, "Singleton object must not be null");
synchronized (this.singletonObjects) {
Object oldObject = this.singletonObjects.get(beanName);
if (oldObject != null) {
throw new IllegalStateException("Could not register object [" + singletonObject +
"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
}
addSingleton(beanName, singletonObject);
}
}
......
/**
* Add the given singleton object to the singleton cache of this factory.
* <p>To be called for eager registration of singletons.
* @param beanName the name of the bean
* @param singletonObject the singleton object
*/
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);
}
}
......
}
还有个问题,第三级缓存中为什么要添加 ObjectFactory 对象,直接保存实例对象不行吗? 真的不行,因为假如你想对添加到三级缓存中的实例对象进行增强,直接用实例对象是行不通 的。针对这种场景spring是怎么做的呢? 答案就在 AbstractAutowireCapableBeanFactory 类 doCreateBean 方法的这段代码中:
// 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));
}
它定义了一个匿名内部类,通过 getEarlyBeanReference 方法获取代理对象,其实底层是通过 AbstractAutoProxyCreator 类的 getEarlyBeanReference 生成代理对象。
到此大家应该明白整个循环依赖解决的过程了吧。多看源码多思考,加油!