这里写自定义目录标题

  • 循环依赖主要场景:
  • 构造方法循环依赖(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循环调用 spring是怎么解决循环依赖_java

构造方法循环依赖(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的创建工厂,以便于后面扩展有机会

创建代理对象。

spring循环调用 spring是怎么解决循环依赖_spring循环调用_02

三级缓存是singletonFactories ,但是是不完整的Bean的工厂Factory,是当一个Bean在new之后 (没有属性填充、初始化),就put进去。所以,是原材料工厂 二级缓存是对三级缓存的过渡性处理,只要通过 getObject() 方法从三级缓存的BeanFactory中 取出Bean一次,原材料就变成变成品,就put到二级缓存 , 所以,二级缓存里边的bean,都是半 成品 一级缓存里面是完整的Bean,是当一个Bean完全创建后(完成属性填充、彻底的完成初始化)才put 进去, 所以,是成品

一张图描述整个过程

spring循环调用 spring是怎么解决循环依赖_spring循环调用_03

那么说到这里,有可能会有人觉得二级缓存不重要如果没有二级缓存行不行???

假设不用第二级缓存,TestService1注入到TestService3的流程如图:

spring循环调用 spring是怎么解决循环依赖_spring循环调用_04

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 生成代理对象。

到此大家应该明白整个循环依赖解决的过程了吧。多看源码多思考,加油!