Spring Bean 循环依赖详解

什么是循环依赖?

循环依赖就是循环引用,就是两个或多个bean相互之间的持有对方。比如CircleA引用CircleB,CircleB引用CircleC,CircleC引用CircleA。

graph LR; A-->B B-->C C-->A

Spring如何解决循环依赖?

Spring容器循环依赖包括如下两种:

  1. 构造器循环依赖
  2. setter循环依赖

构造器循环依赖,表示通过构造器注入的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常。

原型模式下的依赖检查,只有单例模式下才会尝试解决循环依赖,原型模式下直接抛出异常BeanCurrentlyInCreationException异常。

scss 复制代码
AbstractBeanFactory抽象类:

// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}

单例模式下,setter循环依赖可以解决(使用@Async的单例除外,特殊情况下会报错)

单例模式下,构造器循环依赖无法解决

原型模式下,循环依赖无法解决

Spring 循环依赖场景分析

不管是单例 Bean还是prototype Bean,注入Bean的逻辑起点都是在AbstractBeanFactory.java中的 getBean方法。

以a,b 两个bean互相依赖为例,假设Spring容器先加载 a 后加载 b,时序图如下:

sequenceDiagram AbstractBeanFactory.java->>DefaultSingletonBeanRegistry.java:getBean("a") AbstractBeanFactory.java->>DefaultSingletonBeanRegistry.java:getSingleton("a", true) DefaultSingletonBeanRegistry.java ->> DefaultSingletonBeanRegistry.java:this.singletonFactories.get("a")为空 DefaultSingletonBeanRegistry.java-->>AbstractBeanFactory.java: 返回 sharedInstance "a" 为空(缓存里没有) AbstractBeanFactory.java->>DefaultSingletonBeanRegistry.java: getSingleton("a", ObjectFactory singletonFactory) DefaultSingletonBeanRegistry.java -->> DefaultSingletonBeanRegistry.java: singletonFactory.getObject() DefaultSingletonBeanRegistry.java->>AbstractAutowireCapableBeanFactory: createBean("a", mbd, args) AbstractAutowireCapableBeanFactory-->>AbstractAutowireCapableBeanFactory: doCreateBean("a", mbdToUse, args) AbstractAutowireCapableBeanFactory-->>AbstractAutowireCapableBeanFactory:createBeanInstance("a", mbd, args) 实例化 AbstractAutowireCapableBeanFactory-->>AbstractAutowireCapableBeanFactory:addSingletonFactory("a", singletonFactory) 添加三级缓存工厂方法 AbstractAutowireCapableBeanFactory-->>AbstractAutowireCapableBeanFactory:populateBean,寻找注入所依赖的"b" AbstractAutowireCapableBeanFactory-->> AbstractBeanFactory.java: getBean("b") AbstractBeanFactory.java->>DefaultSingletonBeanRegistry.java:getSingleton("b", true) DefaultSingletonBeanRegistry.java-->>AbstractBeanFactory.java: 返回 sharedInstance "b" 为空(缓存里没有) AbstractBeanFactory.java->>DefaultSingletonBeanRegistry.java: getSingleton("b", ObjectFactory singletonFactory) DefaultSingletonBeanRegistry.java -->> DefaultSingletonBeanRegistry.java: singletonFactory.getObject() DefaultSingletonBeanRegistry.java->>AbstractAutowireCapableBeanFactory: createBean("b", mbd, args) AbstractAutowireCapableBeanFactory-->>AbstractAutowireCapableBeanFactory: doCreateBean("b", mbdToUse, args) AbstractAutowireCapableBeanFactory-->> AbstractAutowireCapableBeanFactory:createBeanInstance("b", mbd, args) 实例化 AbstractAutowireCapableBeanFactory-->> AbstractAutowireCapableBeanFactory:addSingletonFactory("b", singletonFactory) 添加三级缓存工厂方法 AbstractAutowireCapableBeanFactory-->> AbstractAutowireCapableBeanFactory:populateBean,寻找注入所依赖的"a" AbstractAutowireCapableBeanFactory -->> AbstractBeanFactory.java: getBean("a") AbstractBeanFactory.java->> DefaultSingletonBeanRegistry.java:getBean("a") AbstractBeanFactory.java->>DefaultSingletonBeanRegistry.java:getSingleton("a", true) DefaultSingletonBeanRegistry.java -->>DefaultSingletonBeanRegistry.java:this.singletonFactories.get("a")(三级缓存有a值) DefaultSingletonBeanRegistry.java -->> DefaultSingletonBeanRegistry.java:singletonFactory.getObject() DefaultSingletonBeanRegistry.java->> AbstractAutowireCapableBeanFactory: getEarlyBeanReference 获取早期的Bean "a" AbstractAutowireCapableBeanFactory -->> AbstractAutowireCapableBeanFactory: 若有AOP,会提前处理AOP相关逻辑 AbstractAutowireCapableBeanFactory -->> DefaultSingletonBeanRegistry.java: exposedObject DefaultSingletonBeanRegistry.java -->>DefaultSingletonBeanRegistry.java: 把 Bean "a" 放到二级缓存,移除三级缓存 DefaultSingletonBeanRegistry.java -->> AbstractBeanFactory.java:返回提早暴露的Bean "a" AbstractBeanFactory.java ->> AbstractAutowireCapableBeanFactory: 返回Bean "a",将"a"注入到 "b"当中(b终于找到a了) AbstractAutowireCapableBeanFactory -->> AbstractAutowireCapableBeanFactory:initializeBean("b", exposedObject, mbd),有AOP的话,会在这里处理 AbstractAutowireCapableBeanFactory-->>AbstractAutowireCapableBeanFactory:Object earlySingletonReference = getSingleton("b", false) 为null,因为二级缓存中为空 AbstractAutowireCapableBeanFactory-->>AbstractAutowireCapableBeanFactory:exposedObject != earlySingletonReference 返回 b AbstractAutowireCapableBeanFactory -->> DefaultSingletonBeanRegistry.java:返回 DefaultSingletonBeanRegistry.java -->> DefaultSingletonBeanRegistry.java:addSingleton("b", singletonObject),放到一级缓存中 DefaultSingletonBeanRegistry.java -->> AbstractBeanFactory.java: 返回b (getSingleton("b", ObjectFactory singletonFactory)) AbstractBeanFactory.java ->> AbstractAutowireCapableBeanFactory: a终于找到了依赖的b AbstractAutowireCapableBeanFactory -->> AbstractAutowireCapableBeanFactory:initializeBean("a", exposedObject, mbd),有AOP的话会在此处理 AbstractAutowireCapableBeanFactory-->>AbstractAutowireCapableBeanFactory:Object earlySingletonReference = getSingleton("a", false),这里返回 a 不是null,因为二级缓存里有a AbstractAutowireCapableBeanFactory-->>AbstractAutowireCapableBeanFactory:exposedObject = earlySingletonReference 返回 a AbstractAutowireCapableBeanFactory -->> AbstractBeanFactory.java:返回a

从上面的时序图可以看出主要的流程,涉及到 AbstractBeanFactory、DefaultSingletonBeanRegistry、AbstractAutowireCapableBeanFactory 三个类。

java 复制代码
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.java
java 复制代码
/**
	 * 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
	 */
	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 != NULL_OBJECT ? singletonObject : null);
	}

bean加载时,从一开始的 调用 getSingleton() 方法从单例缓存中获取说起。

这个方法中涉及到三个缓存:

java 复制代码
	/** Cache of singleton objects: bean name --> bean instance */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

	/** Cache of singleton factories: bean name --> ObjectFactory */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

	/** Cache of early singleton objects: bean name --> bean instance */
	private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
  • singletonObjects 一级缓存,存放最终版Bean
  • earlySingletonObjects 二级缓存,存放单例工厂创建的对象
  • singletonFactories 三级缓存,存放单例工厂(是一个函数)

这就是我们常说的三级缓存,第一级是singletonObjects ,第二级是earlySingletonObjects ,第三级是 singletonFactories

getSingleton()方法整个过程如下:

首先从单例对象缓存singletonObjects 中获取,如果没有,且通过isSingletonCurrentlyInCreation() 方法判断当前要获取的bean正在处于创建中,那么就从earlySingletonObjects 二级缓存中获取,如果还没有

则从singletonFactories 三级缓存中获取。获取到的话,就通过singletonFactory.getObject()获取对象,并将其加入到二级缓存当中,从三级缓存当中删除。

这时候肯定有疑问,缓存中的数据是从哪里来的呢?

一直往下跟代码我们会发现doCreateBean() 方法

java 复制代码
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
						@Override
						public Object getObject() throws BeansException {
							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;
							}
						}
					});

AbstractAutowireCapableBeanFactory 中:

java 复制代码
/**
	 * Central method of this class: creates a bean instance,
	 * populates the bean instance, applies post-processors, etc.
	 * @see #doCreateBean
	 */
	@Override
	protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
			throws BeanCreationException {

		if (logger.isDebugEnabled()) {
			logger.debug("Creating instance of bean '" + beanName + "'");
		}
		// Make sure bean class is actually resolved at this point.
		resolveBeanClass(mbd, beanName);

		// Prepare method overrides.
		try {
			mbd.prepareMethodOverrides();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(mbd.getResourceDescription(),
					beanName, "Validation of method overrides failed", ex);
		}

		try {
			// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
			Object bean = resolveBeforeInstantiation(beanName, mbd);
			if (bean != null) {
				return bean;
			}
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"BeanPostProcessor before instantiation of bean failed", ex);
		}

		Object beanInstance = doCreateBean(beanName, mbd, args);
		if (logger.isDebugEnabled()) {
			logger.debug("Finished creating instance of bean '" + beanName + "'");
		}
		return beanInstance;
	}

doCreateBean():

java 复制代码
// 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.isDebugEnabled()) {
				logger.debug("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, new ObjectFactory<Object>() {
				@Override
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});
		}

我们可以看到 earlySingletonExposuretrue 的时候,才会调用addSingletonFactory()将bean对象加入到三级缓存当中。

earlySingletonExposure 为true 的前提是:

  • bean是单例的
  • bean 允许提前引用
  • 当前bean正在创建当中

addSingletonFactory() 代码如下:

java 复制代码
	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);
			}
		}
	}	

将bean添加到三级缓存,从二级缓存中删掉

那么一级缓存是在哪里添加的呢?

前边我们看到的 doCreateBean() 方法其实来自匿名内部类 new ObjectFactory<Object>() {} 重写的getObject() 方法

java 复制代码
// Create bean instance.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
						@Override
						public Object getObject() throws BeansException {
							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;
							}
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

在调用 getSingleton() 方法时, getSingleton() 方法中( DefaultSingletonBeanRegistry ),调用了上边重写的getObject() 方法后,后边调用了addSingleton(beanName, singletonObject);

addSingleton(beanName, singletonObject) 方法如下:

java 复制代码
	/**
	 * 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 != null ? singletonObject : NULL_OBJECT));
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

添加到一级缓存,从二级缓存中删掉,从三级缓存中删掉,将已经创建成功的bean添加registeredSingletons

集合中。

总结

​ 至此,Spring 关于 singleton bean 循环依赖已经分析完毕了。所以我们基本上可以确定 Spring 解决循环依赖的方案了:Spring 在创建 bean 的时候并不是等它完全完成,而是在创建过程中将创建中的 bean 的 ObjectFactory 提前曝光(即加入到 singletonFactories 缓存中,代码在上文的**"addSingletonFactory() 代码"**),这样一旦下一个 bean 创建的时候需要依赖 bean ,则直接使用 ObjectFactory 的 getObject() 获取了,也就是 getSingleton() 中的代码片段了。

​ 以A,B两个Bean互相依赖为例,在获取A Bean的时候,发现三级缓存中都是空的,则走后续实例化Bean的逻辑,将自己的ObjectFactory放到三级缓存当中,再接着进行加载自己依赖的其他属性,这时候去加载B Bean,同样的流程,B Bean也将自己的ObjectFactory放入到三级缓存中,再接着加载自己依赖的A Bean。这时候从三级缓存中获取到 A Bean ,同时将获取到的Bean放到二级缓存中。继续走后面的初始化逻辑。这时B就加载到了早期的A Bean。A Bean 继续走后续逻辑就成了一个完整的Bean。

​ 到这里,关于 Spring 解决 bean 循环依赖就已经分析完毕了。最后来描述下就上面那个循环依赖 Spring 解决的过程:首先 A 完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),在初始化的时候,发现自己依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 还没有被创建出来,然后 B 就走创建流程,在 B 初始化的时候,同样发现自己依赖 C,C 也没有被创建出来,这个时候 C 又开始初始化进程,但是在初始化的过程中发现自己依赖 A,于是尝试 get(A),这个时候由于 A 已经添加至缓存中(一般都是添加至三级缓存 singletonFactories ),通过 ObjectFactory 提前曝光,所以可以通过 ObjectFactory.getObject() 拿到 A 对象,C 拿到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中,回到 B ,B 也可以拿到 C 对象,完成初始化,A 可以顺利拿到 B 完成初始化。到这里整个链路就已经完成了初始化过程了。

参考

相关推荐
zhenxin01225 小时前
Spring Boot 3.x 系列【3】Spring Initializr快速创建Spring Boot项目
spring boot·后端·spring
oyzz1206 小时前
Spring EL 表达式的简单介绍和使用
java·后端·spring
后置的猿猴7 小时前
Spring 循环依赖
java·后端·spring
热爱Java,热爱生活7 小时前
浅谈Spring三级缓存
java·spring·缓存
shark222222210 小时前
Spring 的三种注入方式?
java·数据库·spring
hERS EOUS10 小时前
Spring Boot + Spring AI快速体验
人工智能·spring boot·spring
JAVA学习通10 小时前
LangChain4j 与 Spring AI 的技术选型深度对比:2026 年 Java AI 工程化实践指南
java·人工智能·spring
yaodong51811 小时前
Spring 中使用Mybatis,超详细
spring·tomcat·mybatis
splage11 小时前
Spring Cloud Data Flow 简介
后端·spring·spring cloud
zuowei288913 小时前
spring实例化对象的几种方式(使用XML配置文件)
xml·java·spring