【Java】Spring框架是如何解决Bean创建过程中的循环依赖问题的

文章目录

  • 【Java】Spring框架是如何解决Bean创建过程中的循环依赖问题的
    • 引言
    • 三级缓存数据结构简介
    • 源码阅读
      • 如何启动调试?
        • 场景1:通过xml配置文件来构造循环引用
        • 场景2:通过注解+自动装配属性来构造循环引用:以`@Controller`为例
      • 脉络
      • 场景1的执行过程
      • `@Controller`+自动装配属性和普通Bean解决循环依赖过程的相同点与不同点
    • 一些扩展结论
    • 总结
    • 参考资料

【Java】Spring框架是如何解决Bean创建过程中的循环依赖问题的

引言

本文主要梳理了Spring框架Bean创建过程中应对循环依赖问题的相关源码。我在手写super-mini-webpack的时候也介绍过解决循环依赖的算法:Map+记忆化搜索。可以猜测这段源码也实现了这个算法,所以在看这段源码的时候,我们可以先找到递归点,再去分析调用栈涉及的那些函数,顺便找出其用到的Map数据结构。

作者:hans774882968以及hans774882968以及hans774882968

本文所用工程

本文CSDN

本文juejin

本文52pojie:https://www.52pojie.cn/thread-1814835-1-1.html

三级缓存数据结构简介

三级缓存数据结构定义和操作三级缓存的函数都位于:spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java

	/** Cache of singleton objects: bean name to bean instance. */private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);/** Cache of singleton factories: bean name to ObjectFactory. */private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);/** Cache of early singleton objects: bean name to bean instance. */private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

缓存先找到第一级,第一级没有才查第二级,依此类推。singletonObjects, earlySingletonObjects, singletonFactories分别是1到3级。第一级缓存是bean名到成品bean的映射;第二级缓存是bean名到半成品bean的映射;第三级缓存是bean名到函数式接口的映射,作用为延迟调用函数。

@FunctionalInterface
public interface ObjectFactory<T> {/*** Return an instance (possibly shared or independent)* of the object managed by this factory.* @return the resulting instance* @throws BeansException in case of creation errors*/T getObject() throws BeansException;}

第三级缓存的Value里的函数的调用方式就是调用.getObject()

为什么需要2级缓存?因为要体现一个分层的思想,半成品bean原则上是不能暴露到外部的。TODO:外部是指?

为什么需要3级缓存?如果所有bean都没有代理对象就不需要第3级缓存。TODO:补充说明。

源码阅读

如何启动调试?

新建一个普通的SpringBoot项目。然后可以尝试通过xml配置文件和注解等方式构造循环引用。后文称为场景1和场景2。

场景1:通过xml配置文件来构造循环引用

入口src\main\java\com\hans\bean_dependency_cycle\hans\HansApplication.java

package com.hans.bean_dependency_cycle.hans;import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;@SpringBootApplication
public class HansApplication {public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("cycle.xml");A beanA = ac.getBean(A.class);System.out.println(beanA);System.out.println(beanA.getB());B beanB = ac.getBean(B.class);System.out.println(beanB);System.out.println(beanB.getA());}}

src\main\resources\cycle.xml

<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"
><bean id="a" class="com.hans.bean_dependency_cycle.hans.A"><property name="b" ref="b"></property></bean><bean id="b" class="com.hans.bean_dependency_cycle.hans.B"><property name="a" ref="a"></property></bean>
</beans>

A.java和B.java:

// A 和 B 不能都使用 lombok,否则无法打印。这里选择了 B 不使用 lombok
package com.hans.bean_dependency_cycle.hans;import lombok.Data;@Data
public class A {private B b;public String toString(A o) {return o.getClass().getName() + "@" +Integer.toHexString(System.identityHashCode(o));}}package com.hans.bean_dependency_cycle.hans;public class B {private A a;public A getA() {return this.a;}public void setA(A a) {this.a = a;}}

期望输出:

A(b=com.hans.bean_dependency_cycle.hans.B@750e2b97)
com.hans.bean_dependency_cycle.hans.B@750e2b97
com.hans.bean_dependency_cycle.hans.B@750e2b97
A(b=com.hans.bean_dependency_cycle.hans.B@750e2b97)

场景1不需要配置spring.main.allow-circular-references为true也能得到期望输出,TODO:原因未知。

场景2:通过注解+自动装配属性来构造循环引用:以@Controller为例

入口src\main\java\com\hans\bean_dependency_cycle\hans\AnotherEntry.java

package com.hans.bean_dependency_cycle.hans;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class AnotherEntry {public static void main(String[] args) {ConfigurableApplicationContext cac = SpringApplication.run(AnotherEntry.class, args);ControllerA beanA = cac.getBean(ControllerA.class);System.out.println(beanA);System.out.println(beanA.getCb());ControllerB beanB = cac.getBean(ControllerB.class);System.out.println(beanB);System.out.println(beanB.getCa());}
}

根据参考链接1,接下来一定要记得修改application.yml

spring:main:allow-circular-references: true

否则会报错:

***************************
APPLICATION FAILED TO START
***************************Description:The dependencies of some of the beans in the application context form a cycle:┌─────┐
|  controllerA (field private com.hans.bean_dependency_cycle.hans.ControllerB com.hansn_dependency_cycle.hans.ControllerA.cb)
↑     ↓
|  controllerB (field private com.hans.bean_dependency_cycle.hans.ControllerA com.hansn_dependency_cycle.hans.ControllerB.ca)
└─────┘Action:Relying upon circular references is discouraged and they are prohibited by default. Up your application to remove the dependency cycle between beans. As a last resort, it me possible to break the cycle automatically by setting spring.main.allow-circular-refees to true.

两个普通的Controller:

// src\main\java\com\hans\bean_dependency_cycle\hans\ControllerA.java
package com.hans.bean_dependency_cycle.hans;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class ControllerA {@Autowiredprivate ControllerB cb;public ControllerB getCb() {return cb;}@RequestMapping("/controllerA")public String index() {return "hello controllerA!";}
}// src\main\java\com\hans\bean_dependency_cycle\hans\ControllerB.java
package com.hans.bean_dependency_cycle.hans;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class ControllerB {@Autowiredprivate ControllerA ca;public ControllerA getCa() {return ca;}@RequestMapping("/controllerB")public String index() {return "hello controllerB!";}
}

期望输出:

com.hans.bean_dependency_cycle.hans.ControllerA@39f82681
com.hans.bean_dependency_cycle.hans.ControllerB@4bd9e7fd
com.hans.bean_dependency_cycle.hans.ControllerB@4bd9e7fd
com.hans.bean_dependency_cycle.hans.ControllerA@39f82681

脉络

根据参考链接2,有一个经验:“do”开头的方法名是真正含有大量逻辑的方法。参考链接2Java之父精选的脉络函数如下:

  • getBean
  • doGetBean
  • createBean
  • doCreateBean
  • createBeanInstance
  • populateBean

场景1到递归点为止的调用链:preInstantiateSingletons > getBean > doGetBean > getSingleton函数有多个,其中调用了beforeSingletonCreation的函数调用singletonFactory.getObject()时才真正调用了createBean > createBean > doCreateBean > createBeanInstance, populateBean 都在 doCreateBean 的实现里 > populateBean调用了applyPropertyValues > resolveValueIfNecessary > resolveReference > getBean

场景2的调用链到populateBean开始和场景1的调用链岔开,两个场景的差异放在后文《Controller和普通Bean解决循环依赖过程的相同点与不同点》分析。

场景1递归到A -> B -> A时的调用链:getBean > doGetBean > 未调用beforeSingletonCreation的getSingleton,操作第2级缓存后离开

操作三级缓存的函数都位于https://github.com/spring-projects/spring-framework/blob/502997d8e986dcfde1f49b2b2f443a32b5488b13/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java

  1. 放入第3级缓存的函数:doCreateBean调用的addSingletonFactory
  2. 放入第2级缓存的函数:首先要知道getSingletonDefaultSingletonBeanRegistry.java里本质上的实现有两个,一个public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)调用了beforeSingletonCreation并间接调用了createBean;另一个protected Object getSingleton(String beanName, boolean allowEarlyReference)则是操作了第2级缓存。
  3. 放入第1级缓存的函数:addSingleton。在间接调用了createBean函数的getSingleton处调用。

场景1的执行过程

真正意义上的入口:spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java的beanFactory.preInstantiateSingletons();。这里的beanFactory就有singletonObjects那3级缓存的对象。于是跳到https://github.com/spring-projects/spring-framework/blob/bbde68c49e66c3c531920cb80a55742262507be7/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java

	@Overridepublic void preInstantiateSingletons() throws BeansException {if (logger.isTraceEnabled()) {logger.trace("Pre-instantiating singletons in " + this);}// Iterate over a copy to allow for init methods which in turn register new bean definitions.// While this may not be part of the regular factory bootstrap, it does otherwise work fine.List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);// 触发所有非延迟加载的(non-lazy)单例 bean 的初始化// Trigger initialization of all non-lazy singleton beans...for (String beanName : beanNames) {// 假如先遍历 A 再遍历 B 那么遍历到 B 的时候,因为循环引用解决的关系,B 已经放到了第1级缓存,所以 doGetBean 的 getSingleton 可以直接从第1级缓存取到值,不用再走一遍 createBean 方法// 合并父类 BeanDefinitionRootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);// 非抽象、是单例、非懒加载if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {// 如果实现了 FactoryBean 接口则是 FactoryBeanif (isFactoryBean(beanName)) {Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); // 比如:FACTORY_BEAN_PREFIX + beanName = "&A"if (bean instanceof SmartFactoryBean<?> smartFactoryBean && smartFactoryBean.isEagerInit()) {getBean(beanName);}}else {// 不是 FactoryBean,只是普通 Bean,则走这个分支getBean(beanName);}}}// 触发所有 SmartInitializingSingleton 的后初始化回调// Trigger post-initialization callback for all applicable beans...for (String beanName : beanNames) {Object singletonInstance = getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) {StartupStep smartInitialize = getApplicationStartup().start("spring.beans.smart-initialize").tag("beanName", beanName);smartSingleton.afterSingletonsInstantiated();smartInitialize.end();}}}

顺便看下isFactoryBean的实现https://github.com/spring-projects/spring-framework/blob/4786e2bf53a3f882c10e25d7ff79a18ff47b5e51/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java

	@Overridepublic boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {String beanName = transformedBeanName(name);Object beanInstance = getSingleton(beanName, false);if (beanInstance != null) {return (beanInstance instanceof FactoryBean);}// No singleton instance found -> check bean definition.if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory cbf) {// No bean definition found in this factory -> delegate to parent.return cbf.isFactoryBean(name);}return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));}

getBeanisFactoryBean都位于https://github.com/spring-projects/spring-framework/blob/4786e2bf53a3f882c10e25d7ff79a18ff47b5e51/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.javagetBean只有1行

	@Overridepublic Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false);}

doGetBeangetBean, isFactoryBean都在AbstractBeanFactory.java

	/*** Return an instance, which may be shared or independent, of the specified bean.* @param name the name of the bean to retrieve* @param requiredType the required type of the bean to retrieve* @param args arguments to use when creating a bean instance using explicit arguments* (only applied when creating a new instance as opposed to retrieving an existing one)* @param typeCheckOnly whether the instance is obtained for a type check,* not for actual use* @return an instance of the bean* @throws BeansException if the bean could not be created*/@SuppressWarnings("unchecked")protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {String beanName = transformedBeanName(name);Object beanInstance;// 提前检查单例缓存中是否有手动注册的单例对象,跟循环依赖有关。如果是初次进这里,比如 controllerA 初次进这个方法,肯定是拿不到值的,就会是 null// 对于最简单的循环依赖,controllerA -> controllerB -> controllerA 之后,需要进入 getSingleton 了,这里的逻辑就是要从第3级缓存里拿到工厂函数,调用后得到 controllerA 半成品,从而可以直接 return// Eagerly check singleton cache for manually registered singletons.Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {if (logger.isTraceEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {// Fail if we're already creating this bean instance:// We're assumably within a circular reference.if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// Check if bean definition exists in this factory.BeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// Not found -> check parent.String nameToLookup = originalBeanName(name);if (parentBeanFactory instanceof AbstractBeanFactory abf) {return abf.doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}else if (args != null) {// Delegation to parent with explicit args.return (T) parentBeanFactory.getBean(nameToLookup, args);}else if (requiredType != null) {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);}else {return (T) parentBeanFactory.getBean(nameToLookup);}}if (!typeCheckOnly) {markBeanAsCreated(beanName);}StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate").tag("beanName", name);try {if (requiredType != null) {beanCreation.tag("beanType", requiredType::toString);}RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// Guarantee initialization of beans that the current bean depends on.String[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {for (String dep : dependsOn) {if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}registerDependentBean(dep, beanName);try {getBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}// Create bean instance.if (mbd.isSingleton()) {// 返回 beanName 的原始单例对象。如果尚未注册,则使用 singletonFactory 创建并注册一个对象sharedInstance = getSingleton(beanName, () -> {try {// 为给定的合并后的 BeanDefinition 和参数创建一个 bean 实例// createBean 真实执行时机是调用了 beforeSingletonCreation 方法的 getSingleton 方法执行 singletonObject = singletonFactory.getObject() 时// 首次运行到 beanName = "controllerA" 时 args = nullreturn 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.// 显式地从单例缓存中删除bean实例:因为这个实例可能是由创建过程急切地放在那里的,放在那里的目的是允许循环引用解析。// 还要删除所有被这个bean临时引用的所有bean。如果找到相应的一次性bean实例,则委托给 destroyBeandestroySingleton(beanName);throw ex;}});beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}else if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}else {String scopeName = mbd.getScope();if (!StringUtils.hasLength(scopeName)) {throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");}Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}});beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new ScopeNotActiveException(beanName, scopeName, ex);}}}catch (BeansException ex) {beanCreation.tag("exception", ex.getClass().toString());beanCreation.tag("message", String.valueOf(ex.getMessage()));cleanupAfterBeanCreationFailure(beanName);throw ex;}finally {beanCreation.end();}}return adaptBeanInstance(name, beanInstance, requiredType);}

我们先只关注createBean,所以需要关注getSingleton。注意:

getSingleton的函数在DefaultSingletonBeanRegistry.java里本质上的实现有两个,一个调用了beforeSingletonCreation并间接调用了createBean;另一个则是操作了第2级缓存。

所以目前我们需要关注的是调用了beforeSingletonCreationgetSingleton方法。路径:https://github.com/spring-projects/spring-framework/blob/502997d8e986dcfde1f49b2b2f443a32b5488b13/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java

	/*** Return the (raw) singleton object registered under the given name,* creating and registering a new one if none registered yet.* @param beanName the name of the bean* @param singletonFactory the ObjectFactory to lazily create the singleton* with, if necessary* @return the registered singleton object*/public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");synchronized (this.singletonObjects) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {if (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName,"Singleton bean creation not allowed while singletons of this factory are in destruction " +"(Do not request a bean from a BeanFactory in a destroy method implementation!)");}if (logger.isDebugEnabled()) {logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}// 创建单例前的回调,默认实现为:将单例注册为当前正在创建中,实现只有3行,可以看下。beforeSingletonCreation(beanName);// flag 表示是否生成了新的单例对象boolean newSingleton = false;// flag 表示是否有抑制异常的记录,true表示没有boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {// 若没有抑制异常记录,则对抑制异常列表进行初始化this.suppressedExceptions = new LinkedHashSet<>();}try {// 从单例工厂获取对象。注意 singletonFactory 是本方法第二个参数,之前也介绍了// ObjectFactory 对象通过调 getObject 来正式执行函数,所以 createBean 在此时才真正执行singletonObject = singletonFactory.getObject();// 获取后,就已经生成了新的单例对象,标记为 truenewSingleton = true;}catch (IllegalStateException ex) {// Has the singleton object implicitly appeared in the meantime ->// if yes, proceed with it since the exception indicates that state.singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {throw ex;}}catch (BeanCreationException ex) {if (recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {ex.addRelatedCause(suppressedException);}}throw ex;}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}afterSingletonCreation(beanName);}if (newSingleton) {// 操作第1级缓存addSingleton(beanName, singletonObject);}}return singletonObject;}}

在调用singletonObject = singletonFactory.getObject();以间接调用createBean后,会调用addSingleton,将bean加入第1级缓存,这标志着bean变为成品。接下来我们看下addSingleton的代码,它和getSingleton定义于同一个文件。

	/*** 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) {// addSingleton 在调用了 createBean 的 getSingleton 方法中被调用,标志着 bean 变为成品对象synchronized (this.singletonObjects) {// 第1级缓存添加,第2、3级缓存移除this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);// 添加到已注册的单例集合。 registeredSingletons 为 Set<String>this.registeredSingletons.add(beanName);}}

接下来看createBeanhttps://github.com/spring-projects/spring-framework/blob/4786e2bf53a3f882c10e25d7ff79a18ff47b5e51/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java

	/*** Central method of this class: creates a bean instance,* populates the bean instance, applies post-processors, etc.* @see #doCreateBean*/@Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// 首次看源码,直接看 doCreateBean 调用处if (logger.isTraceEnabled()) {logger.trace("Creating instance of bean '" + beanName + "'");}RootBeanDefinition mbdToUse = mbd;// Make sure bean class is actually resolved at this point, and// clone the bean definition in case of a dynamically resolved Class// which cannot be stored in the shared merged bean definition.Class<?> resolvedClass = resolveBeanClass(mbd, beanName);if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {mbdToUse = new RootBeanDefinition(mbd);mbdToUse.setBeanClass(resolvedClass);}// Prepare method overrides.try {mbdToUse.prepareMethodOverrides();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbdToUse.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, mbdToUse);if (bean != null) {return bean;}}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,"BeanPostProcessor before instantiation of bean failed", ex);}try {// 实际创建 beanObject beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isTraceEnabled()) {logger.trace("Finished creating instance of bean '" + beanName + "'");}return beanInstance;}catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {// A previously detected exception with proper bean creation context already,// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.throw ex;}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);}}

createBean目前唯一值得关注的点就是调用了doCreateBeandoCreateBean做了几件值得本文关注的事:

  • 调用了createBeanInstance完成bean的实例化。
  • 调用了addSingletonFactory,即加入了第3级缓存。
  • 调用了populateBean完成bean的属性赋值操作。

doCreateBeancreateBean都定义在https://github.com/spring-projects/spring-framework/blob/4786e2bf53a3f882c10e25d7ff79a18ff47b5e51/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java

	/*** 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.// instanceWrapper 持有创建出的 bean 对象BeanWrapper instanceWrapper = null;// 获取 factoryBean 实例缓存if (mbd.isSingleton()) {// 如果是单例对象,从 factoryBean 实例缓存中移除当前 bean 的信息// 首次 controllerA 进来会进行移除操作,并且会调用 createBeanInstanceinstanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}// 没有就创建实例if (instanceWrapper == null) {// 根据执行 bean 使用对应的策略创建新的工厂实例,如:工厂方法、构造函数主动注入、简单初始化// 第一次读源码时不需要点进去看 createBeanInstance 。下一个主干方法 populateBean 是在本函数下文调用的instanceWrapper = createBeanInstance(beanName, mbd, args);}// 从包装类中获取原始 bean 。首次执行 controllerA 的时候, bean 就是普通的 controllerA 对象 ControllerA@100{cb=null}Object bean = instanceWrapper.getWrappedInstance();// 获取具体 bean 对象的 Class 属性Class<?> beanType = instanceWrapper.getWrappedClass();if (beanType != NullBean.class) {// 不等于 NullBean 类型时就修改目标类型mbd.resolvedTargetType = beanType;}// Allow post-processors to modify the merged bean definition.synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.markAsPostProcessed();}}// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.// 判断当前 bean 是否需要提前曝光,条件为:是单例 && 允许循环依赖 spring.main.allow-circular-references 配置为 true && 当前 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");}// 为避免后期循环依赖,可以在 bean 初始化完成前将创建实例的 ObjectFactory 加入工厂// 注意,这个方法会操作3级缓存的数据结构,尤其是第3级缓存。在 controllerA -> controllerB -> controllerA 的时候未调用 createBean 的 getSingleton 方法会真正调用这个匿名函数,从而调用 getEarlyBeanReference 进而操作第2级缓存addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// Initialize the bean instance.// controllerA 首次执行到这里时,exposedObject = ControllerA@100{cb=null}Object exposedObject = bean;try {// populateBean 是主干方法,给刚刚实例化的 bean (半成品)填充属性populateBean(beanName, mbd, instanceWrapper);// controllerA -> controllerB -> controllerA 之后,getSingleton 返回 controllerA 后回到这里, controllerB 的 ca 属性就有值了// 随后递归返回到这句话且调用栈只有 controllerA 的时候,发现两者都有值了exposedObject = initializeBean(beanName, exposedObject, mbd);}catch (Throwable ex) {if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {throw bce;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);}}if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {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);}}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.try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;}

createBeanInstance的细节与本文主题无关,不关注。接下来我们看下addSingletonFactory的实现。addSingletonFactory位于https://github.com/spring-projects/spring-framework/blob/502997d8e986dcfde1f49b2b2f443a32b5488b13/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java,主要动作是操作第3级缓存。

	/*** 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) {// controllerA 首次执行到这里,第一级缓存肯定是查不到的if (!this.singletonObjects.containsKey(beanName)) {// 第3级缓存放入。再回顾一下, singletonFactory存的是 beanName 到一个延迟执行的函数的映射// controllerA 首次执行到这里的时候, singletonFactory = () -> getEarlyBeanReference(beanName, mbd, bean)this.singletonFactories.put(beanName, singletonFactory);// 从早期单例对象的高速缓存(即第2级缓存)移除当前 beanName 对应的缓存对象this.earlySingletonObjects.remove(beanName);// 添加到已注册的单例集合里,和三级缓存无关。值得注意的是, A 首次加入三级缓存时,就是首次加入已注册的单例集合this.registeredSingletons.add(beanName);}}}

接下来关注位于https://github.com/spring-projects/spring-framework/blob/4786e2bf53a3f882c10e25d7ff79a18ff47b5e51/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.javapopulateBeanpopulateBean方法有一个功能是给bean的属性赋值,包含了递归点。在创建Bean的源码中,递归点指的是递归调用getBean方法。

	/*** Populate the bean instance in the given BeanWrapper with the property values* from the bean definition.* @param beanName the name of the bean* @param mbd the bean definition for the bean* @param bw the BeanWrapper with bean instance*/protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {if (bw == null) {if (mbd.hasPropertyValues()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");}else {// Skip property population phase for null instance.return;}}if (bw.getWrappedClass().isRecord()) {if (mbd.hasPropertyValues()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to a record");}else {// Skip property population phase for records since they are immutable.return;}}// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the// state of the bean before properties are set. This can be used, for example,// to support styles of field injection.if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return;}}}PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);int resolvedAutowireMode = mbd.getResolvedAutowireMode();if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {MutablePropertyValues newPvs = new MutablePropertyValues(pvs);// Add property values based on autowire by name if applicable.if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {autowireByName(beanName, mbd, bw, newPvs);}// Add property values based on autowire by type if applicable.if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {autowireByType(beanName, mbd, bw, newPvs);}pvs = newPvs;}if (hasInstantiationAwareBeanPostProcessors()) {if (pvs == null) {pvs = mbd.getPropertyValues();}for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {// 对于 Controller Bean 自动装配属性产生循环依赖的场景,遍历到 AutowiredAnnotationBeanPostProcessor 时,这句话包含递归点PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {return;}pvs = pvsToUse;}}boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);if (needsDepCheck) {PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);checkDependencies(beanName, mbd, filteredPds, pvs);}if (pvs != null) {// 对于普通 Bean 的场景,这句话包含递归点applyPropertyValues(beanName, mbd, bw, pvs);}}

applyPropertyValues也位于https://github.com/spring-projects/spring-framework/blob/4786e2bf53a3f882c10e25d7ff79a18ff47b5e51/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java,主要功能为完成bean初始化。其中,Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue)包含了递归点,通过动调可以验证bw.setPropertyValues(new MutablePropertyValues(deepCopy))真正完成了属性赋值工作。

	/*** Apply the given property values, resolving any runtime references* to other beans in this bean factory. Must use deep copy, so we* don't permanently modify this property.* @param beanName the bean name passed for better exception information* @param mbd the merged bean definition* @param bw the BeanWrapper wrapping the target object* @param pvs the new property values*/protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {// applyPropertyValues 是真正完成赋值操作的函数// 如果 pvs 没有 PropertyValue,就直接结束if (pvs.isEmpty()) {return;}MutablePropertyValues mpvs = null;List<PropertyValue> original;if (pvs instanceof MutablePropertyValues _mpvs) {mpvs = _mpvs;if (mpvs.isConverted()) {// Shortcut: use the pre-converted values as-is.try {bw.setPropertyValues(mpvs);return;}catch (BeansException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", ex);}}// 获取 mpvs 的 PropertyValue 列表original = mpvs.getPropertyValueList();}else {// 获取 pvs 的 PropertyValue 对象数组并转为列表original = Arrays.asList(pvs.getPropertyValues());}// 用户自定义的类型转换器,默认转换器为 bean 的包装类对象TypeConverter converter = getCustomTypeConverter();if (converter == null) {converter = bw;}BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);// Create a deep copy, resolving any references for values.List<PropertyValue> deepCopy = new ArrayList<>(original.size());// resolveNecessary flag 含义为是否还需要解析boolean resolveNecessary = false;for (PropertyValue pv : original) {// 属性已解析过则加入 deepCopyif (pv.isConverted()) {deepCopy.add(pv);}else {String propertyName = pv.getName();// 获取未经类型转换的值Object originalValue = pv.getValue();if (originalValue == AutowiredPropertyMarker.INSTANCE) {Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod();// 如果 setter 方法为空if (writeMethod == null) {// 异常:自动装配标记属性没有写方法throw new IllegalArgumentException("Autowire marker for property without write method: " + pv);}originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true);}// 交由 valueResolver 根据 pv 解析出 originalValue 所封装的对象。注意:这个函数包含递归点Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);Object convertedValue = resolvedValue;// flag 含义为是否可转换: propertyName 是 bw 中的可写属性 && propertyName 不是索引属性或嵌套属性boolean convertible = bw.isWritableProperty(propertyName) &&!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);if (convertible) {// 可转换则将 resolvedValue 转换为指定的目标属性对象convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);}// Possibly store converted value in merged bean definition,// in order to avoid re-conversion for every created bean instance.if (resolvedValue == originalValue) {if (convertible) {pv.setConvertedValue(convertedValue);}deepCopy.add(pv);}else if (convertible && originalValue instanceof TypedStringValue typedStringValue &&!typedStringValue.isDynamic() &&!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {pv.setConvertedValue(convertedValue);deepCopy.add(pv);}else {resolveNecessary = true;deepCopy.add(new PropertyValue(pv, convertedValue));}}}if (mpvs != null && !resolveNecessary) {mpvs.setConverted();}// Set our (possibly massaged) deep copy.try {// 完成属性赋值工作。咱们做个简单的实验,动调执行此句前后各点击调用栈看一次 populateBean 调用处的下一句的 exposedObject 或者 bean 变量,这就证实了 bean 的属性赋值确实是在这句话完成的bw.setPropertyValues(new MutablePropertyValues(deepCopy));}catch (BeansException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);}}

resolveValueIfNecessary位于https://github.com/spring-projects/spring-framework/blob/30d6ec3398ce41add7bc44d360b8fb86ac0264b1/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java。其实现也很长,但目前只需要关注return resolveReference(argName, ref),因为resolveReference包含递归点。

	/*** Given a PropertyValue, return a value, resolving any references to other* beans in the factory if necessary. The value could be:* <li>A BeanDefinition, which leads to the creation of a corresponding* new bean instance. Singleton flags and names of such "inner beans"* are always ignored: Inner beans are anonymous prototypes.* <li>A RuntimeBeanReference, which must be resolved.* <li>A ManagedList. This is a special collection that may contain* RuntimeBeanReferences or Collections that will need to be resolved.* <li>A ManagedSet. May also contain RuntimeBeanReferences or* Collections that will need to be resolved.* <li>A ManagedMap. In this case the value may be a RuntimeBeanReference* or Collection that will need to be resolved.* <li>An ordinary object or {@code null}, in which case it's left alone.* @param argName the name of the argument that the value is defined for* @param value the value object to resolve* @return the resolved object*/@Nullablepublic Object resolveValueIfNecessary(Object argName, @Nullable Object value) {// We must check each value to see whether it requires a runtime reference// to another bean to be resolved.// RuntimeBeanReference:当属性值对象是工厂中另一个 bean 的引用时,使用不可变的占位符类,在运行时进行解析if (value instanceof RuntimeBeanReference ref) {// 解析出对应 ref 所封装的 Bean 的元信息的 Bean 对象。Bean 的元信息:Bean 名,Bean类型。注意,这个函数里包含递归点return resolveReference(argName, ref);}else if (value instanceof RuntimeBeanNameReference ref) {String refName = ref.getBeanName();refName = String.valueOf(doEvaluate(refName));if (!this.beanFactory.containsBean(refName)) {throw new BeanDefinitionStoreException("Invalid bean name '" + refName + "' in bean reference for " + argName);}return refName;}else if (value instanceof BeanDefinitionHolder bdHolder) {// Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.return resolveInnerBean(bdHolder.getBeanName(), bdHolder.getBeanDefinition(),(name, mbd) -> resolveInnerBeanValue(argName, name, mbd));}else if (value instanceof BeanDefinition bd) {return resolveInnerBean(null, bd,(name, mbd) -> resolveInnerBeanValue(argName, name, mbd));}else if (value instanceof DependencyDescriptor dependencyDescriptor) {Set<String> autowiredBeanNames = new LinkedHashSet<>(2);Object result = this.beanFactory.resolveDependency(dependencyDescriptor, this.beanName, autowiredBeanNames, this.typeConverter);for (String autowiredBeanName : autowiredBeanNames) {if (this.beanFactory.containsBean(autowiredBeanName)) {this.beanFactory.registerDependentBean(autowiredBeanName, this.beanName);}}return result;}else if (value instanceof ManagedArray managedArray) {// May need to resolve contained runtime references.Class<?> elementType = managedArray.resolvedElementType;if (elementType == null) {String elementTypeName = managedArray.getElementTypeName();if (StringUtils.hasText(elementTypeName)) {try {elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());managedArray.resolvedElementType = elementType;}catch (Throwable ex) {// Improve the message by showing the context.throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,"Error resolving array type for " + argName, ex);}}else {elementType = Object.class;}}return resolveManagedArray(argName, (List<?>) value, elementType);}else if (value instanceof ManagedList<?> managedList) {// May need to resolve contained runtime references.return resolveManagedList(argName, managedList);}else if (value instanceof ManagedSet<?> managedSet) {// May need to resolve contained runtime references.return resolveManagedSet(argName, managedSet);}else if (value instanceof ManagedMap<?, ?> managedMap) {// May need to resolve contained runtime references.return resolveManagedMap(argName, managedMap);}else if (value instanceof ManagedProperties original) {// Properties original = managedProperties;Properties copy = new Properties();original.forEach((propKey, propValue) -> {if (propKey instanceof TypedStringValue typedStringValue) {propKey = evaluate(typedStringValue);}if (propValue instanceof TypedStringValue typedStringValue) {propValue = evaluate(typedStringValue);}if (propKey == null || propValue == null) {throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,"Error converting Properties key/value pair for " + argName + ": resolved to null");}copy.put(propKey, propValue);});return copy;}else if (value instanceof TypedStringValue typedStringValue) {// Convert value to target type here.Object valueObject = evaluate(typedStringValue);try {Class<?> resolvedTargetType = resolveTargetType(typedStringValue);if (resolvedTargetType != null) {return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);}else {return valueObject;}}catch (Throwable ex) {// Improve the message by showing the context.throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,"Error converting typed String value for " + argName, ex);}}else if (value instanceof NullBean) {return null;}else {return evaluate(value);}}

resolveReferenceresolveValueIfNecessary都位于https://github.com/spring-projects/spring-framework/blob/30d6ec3398ce41add7bc44d360b8fb86ac0264b1/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java。其实现也很长,但目前只需要关注bean = this.beanFactory.getBean(resolvedName),因为这就是递归点。

	/*** Resolve a reference to another bean in the factory.*/@Nullableprivate Object resolveReference(Object argName, RuntimeBeanReference ref) {try {Object bean;Class<?> beanType = ref.getBeanType();// 如果引用来自父工厂if (ref.isToParent()) {// 获取父工厂BeanFactory parent = this.beanFactory.getParentBeanFactory();if (parent == null) {// 没有父工厂则报错:在父工厂中无法解析对 Bean 的引用,因为父工厂就不存在throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,"Cannot resolve reference to bean " + ref +" in parent factory: no parent factory available");}if (beanType != null) {bean = parent.getBean(beanType);}else {bean = parent.getBean(String.valueOf(doEvaluate(ref.getBeanName())));}}else {String resolvedName;if (beanType != null) {// 解析与 beanType 唯一匹配的 bean 实例,包括其 bean 名NamedBeanHolder<?> namedBean = this.beanFactory.resolveNamedBean(beanType);// 让 bean 引用 namedBean 所封装的 bean 对象bean = namedBean.getBeanInstance();resolvedName = namedBean.getBeanName();}else {resolvedName = String.valueOf(doEvaluate(ref.getBeanName()));// 获取 ref 所包装的 Bean 名对应的 Bean 对象// 注意,这就是递归点了bean = this.beanFactory.getBean(resolvedName);}// 注册依赖关系到 Bean 工厂this.beanFactory.registerDependentBean(resolvedName, this.beanName);}if (bean instanceof NullBean) {bean = null;}return bean;}catch (BeansException ex) {throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);}}

B创建过程调用栈是完全一样的,接下来我们假设现在走到了A -> B -> A,回到了doGetBeanObject sharedInstance = getSingleton(beanName);处。此时我们需要关注其实现了:

	/*** 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*/@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lock// 该函数主要是调用放入第3级缓存的 getEarlyBeanReference 并放入第2级缓存// 首先从第1级缓存获取 beanObject singletonObject = this.singletonObjects.get(beanName);// 第1级缓存没有,并且已标记为创建中if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 从第2级缓存获取 bean.因为controllerA -> controllerB -> controllerA 的时候是创建中的 bean,只放到了第3级缓存,所以是查不到的singletonObject = this.earlySingletonObjects.get(beanName);// controllerA -> controllerB -> controllerA 的时候进来, allowEarlyReference 肯定是 trueif (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// Consistent creation of early reference within full singleton lock// 这段做二次确认的代码让我联想到线程安全的单例模式的写法// 从第1级缓存取singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {// 从第2级缓存取singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {// 从第3级缓存取。如果是 controllerA 初次进来,因为 not in creation 所以不会进这里,就算进了这里,因为第3级缓存取不到所以还是会直接 return。如果是 controllerA -> controllerB -> controllerA 则会操作第2级缓存ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 回顾一下,DefaultSingletonBeanRegistry.java 的 addSingletonFactory 函数在操作第3级缓存的时候,放入的匿名函数就是:// singletonFactory = () -> getEarlyBeanReference(beanName, mbd, bean) 所以 getEarlyBeanReference 返回值会在此被放入第2级缓存singletonObject = singletonFactory.getObject();// 为什么是放第2级缓存?因为 getBean 的递归还没返回// 放入第2级缓存后,三级缓存的就可以移除了this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;}

A -> B -> A的递归返回后,A, B两个单例bean都已经是成品,beanName遍历到B的时候,进入getSingleton就能命中第1级缓存了,不用再走一遍createBean方法。

		for (String beanName : beanNames) {// 假如先遍历 A 再遍历 B 那么遍历到 B 的时候,因为循环引用解决的关系,B 已经放到了第1级缓存,所以 doGetBean 的 getSingleton 可以直接从第1级缓存取到值,不用再走一遍 createBean 方法// 合并父类 BeanDefinitionRootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);// 非抽象、是单例、非懒加载if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {// 如果实现了 FactoryBean 接口则是 FactoryBeanif (isFactoryBean(beanName)) {Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); // 比如:FACTORY_BEAN_PREFIX + beanName = "&A"if (bean instanceof SmartFactoryBean<?> smartFactoryBean && smartFactoryBean.isEagerInit()) {getBean(beanName);}}else {// 不是 FactoryBean,只是普通 Bean,则走这个分支getBean(beanName);}}}

至此,场景1的递归点和三级缓存的操作时机都已经清楚了。

@Controller+自动装配属性和普通Bean解决循环依赖过程的相同点与不同点

场景2的Controller使用了@Autowired注解来构造循环依赖。动调可知,这个场景并不是在populateBeanapplyPropertyValues(beanName, mbd, bw, pvs);完成修改的,而是在populateBean的:

			for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {return;}pvs = pvsToUse;}

这个循环里完成属性赋值的。动调发现getBeanPostProcessorCache().instantiationAware有一个元素是AutowiredAnnotationBeanPostProcessor@133(所有元素分别是ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor@129, InfrastructureAdvisorAutoProxyCreator@130, PersistenceExceptionTranslationPostProcessor@131, CommonAnnotationBeanPostProcessor@132, AutowiredAnnotationBeanPostProcessor@133),遍历到这个元素时执行的操作完成了自动装配属性的赋值。那我们跟进去看下。

AutowiredAnnotationBeanPostProcessor.postProcessProperties位于https://github.com/spring-projects/spring-framework/blob/6183f0684684912802021556dce916ba26228c26/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java

	@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs);}catch (BeanCreationException ex) {throw ex;}catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);}return pvs;}

显然是在metadata.inject处完成自动装配的。InjectionMetadata.inject位于https://github.com/spring-projects/spring-framework/blob/2f33e77ab49f136d83b6ebf5eeb72d200fe23c0b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java

	public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Collection<InjectedElement> checkedElements = this.checkedElements;Collection<InjectedElement> elementsToIterate =(checkedElements != null ? checkedElements : this.injectedElements);if (!elementsToIterate.isEmpty()) {for (InjectedElement element : elementsToIterate) {element.inject(target, beanName, pvs);}}}

element.inject最终跳入的是https://github.com/spring-projects/spring-framework/blob/6183f0684684912802021556dce916ba26228c26/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.javaprotected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws ThrowableAutowiredFieldElement, AutowiredMethodElement都有inject方法,显然这个case里我们调用的是AutowiredFieldElementinject方法。

		@Overrideprotected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Field field = (Field) this.member;Object value;if (this.cached) {try {value = resolveCachedArgument(beanName, this.cachedFieldValue);}catch (BeansException ex) {// Unexpected target bean mismatch for cached argument -> re-resolvethis.cached = false;logger.debug("Failed to resolve cached argument", ex);value = resolveFieldValue(field, bean, beanName);}}else {value = resolveFieldValue(field, bean, beanName);}if (value != null) {ReflectionUtils.makeAccessible(field);field.set(bean, value);}}

显然field.set(bean, value);最终完成了属性的自动装配。值得注意的是,动调看到resolveFieldValue获取到的cb是已经装配好的,这里一定存在递归调用。我们用一个简单的动态调试技巧来找到递归点:在执行到value = resolveFieldValue(field, bean, beanName)时,给doGetBean函数下一个临时的断点。得到的调用栈如下:

  1. AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement.inject()
  2. AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement.resolveFieldValue()value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
  3. DefaultListableBeanFactory.javaresolveDependency方法的result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
  4. DefaultListableBeanFactory.javadoResolveDependency方法的instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
  5. https://github.com/spring-projects/spring-framework/blob/f1fe16e3cda66b164f77489f82287116477197bc/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.javaresolveCandidate方法的return beanFactory.getBean(beanName);,这就是递归点了,beanName = controllerB

总而言之,Controller的bean自动装配属性的场景和普通的bean的递归点不一样,但对三级缓存的操作逻辑是完全一致的。谜底已揭晓~

一些扩展结论

【1】为什么Spring不能解决构造器的循环依赖?

doCreateBean调用createBeanInstance时,一二三级缓存都没有Bean的相关信息,在实例化之后才调用addSingletonFactory放入第3级缓存中,因此当getBean的时候缓存不会命中,所以会抛出循环依赖的异常。

【2】为什么多实例Bean不能解决循环依赖?

多实例Bean是每次创建都会调用doGetBean方法,mbd.isSingleton()是false,不走sharedInstance = getSingleton(beanName, () -> {这个分支,所以也不会使用三级缓存,不能解决循环依赖。

总结

  1. 根据常识猜测Spring创建Bean过程解决循环依赖的算法也是Map+记忆化搜索。所以我们可以先找到递归点,再去分析调用栈涉及的那些函数,顺便找出其用到的Map数据结构。
  2. 在不了解Spring框架的情况下可以用一个动态调试技巧快速找到递归点:在执行到某条顺序靠后的语句时,给顺序靠前的语句下一个断点,若下断成功,则说明找到了递归点。剩下的工作就是关注调用栈涉及的函数。

参考资料

  1. 痛快!SpringBoot终于禁掉了循环依赖!https://juejin.cn/post/7096798740593246222
  2. https://www.bilibili.com/video/BV1J14y1A7eE/?p=82 震惊!这位零基础前端同学在学习Java的第7天就可以研究Spring框架源码了!这是怎么做到的呢?噢!原来是站在了Java之父🐎士兵的肩膀上

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/13024.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jdk8使用okhttp发送http2请求

本文主要用于工作记录&#xff0c;在项目中遇到了就记录一下 在早期&#xff0c;原生的JDK8是不支持HTTP/2协议的&#xff0c;所以&#xff0c;要想使用这个特性&#xff0c;需要有web服务器和应用环境的支持&#xff0c; 例如&#xff1a;在VM中增加-Xbootclasspath/p:/Users…

Android Studio 启用设备远程调试配置完整步聚

启用手机设置->开发者选项-无线调试,然后选择允许 已启用后无线调试变成绿色 ,点击无线调试进入详情页面 点击Android Studio的Device Manager 下的WIFI图标 会弹出下图窗口 打开手机的开发者选项中的WIFI调试(无线调试)下的使用二维码配对设备进行扫描. 设备配对成功后手机…

Unity Shader - if 和 keyword 的指令比较

文章目录 环境TestingIf4Sampleunity shaderlab 中的 TestingIf4Sample.shadergraphics analyzer 中的 TestingIf4Sample.glsl TestingKW4Sampleunity shaderlab 中的 TestingKW4Sample.shadergraphics analyzer 中的 TestingKW4Sample.glsl 比较 环境 Unity : 2020.3.37f1 Pi…

在Linux中用strsignal函数输出对各种信号的描述

2023年7月29日&#xff0c;周六上午 目录 函数原型Linux有多少种信号使用示例 函数原型 #include <string.h>char* strsignal(int signum);strsignal函数接受一个整数参数signum&#xff0c;表示信号的编号。 用于把信号编号转换成一个简短的对这个信号编号的描述。 L…

JAVA SE -- 第十一天

&#xff08;全部来自“韩顺平教育”&#xff09; 异常-Exception 一、异常介绍 1、基本介绍 Java语言中&#xff0c;将程序执行中发生的不正常情况为“异常”&#xff08;开发过程中的语法错误和逻辑错误不是异常&#xff09; 2、执行过程中发生的异常事件可分为两大类 …

FPGA2-采集OV5640乒乓缓存后经USB3.0发送到上位机显示

1.场景 基于特权A7系列开发板&#xff0c;采用OV5640摄像头实时采集图像数据&#xff0c;并将其经过USB3.0传输到上位机显示。这是验证数据流能力的很好的项目。其中&#xff0c;用到的软件版本&#xff0c;如下表所示&#xff0c;基本的硬件情况如下。该项目对应FPGA工程源码…

图注意力网络论文详解和PyTorch实现

图神经网络(gnn)是一类功能强大的神经网络&#xff0c;它对图结构数据进行操作。它们通过从节点的局部邻域聚合信息来学习节点表示(嵌入)。这个概念在图表示学习文献中被称为“消息传递”。 消息(嵌入)通过多个GNN层在图中的节点之间传递。每个节点聚合来自其邻居的消息以更新其…

MyBatis(二)

文章目录 一.MyBatis的模式开发1.1 定义数据表和实体类1.2 配置数据源和MyBatis1.3 编写Mapper接口和增加xxxMapper.xml1.4 测试我们功能的是否实现. 二. Mybatis的增删查改操作2.1 单表查询2.2 多表查询三.动态SQL的实现3.1 什么是动态SQL3.2 动态SQL的使用if标签的使用trim标…

M1/M2 通过VM Fusion安装Win11 ARM,解决联网和文件传输

前言 最近新入了Macmini M2&#xff0c;但是以前的老电脑的虚拟机运行不起来了。&#x1f605;&#xff0c;实际上用过K8S的时候&#xff0c;会发现部分镜像也跑不起来&#xff0c;X86的架构和ARM实际上还是有很多隐形兼容问题。所以只能重新安装ARM Win11&#xff0c;幸好微软…

Spring整合junit

1、导入pom坐标 <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>org.springframework</gro…

【LeetCode】169.多数元素

题目 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,3] 输出&#xff1a;3…

【MySQL】表的操作

今天我们来谈谈MySQL下对表的操作 目录 一、创建表 二、查看表 2.1 查看库中存有的表 2.2 查看表结构 2.3 查看表的创建语句 三、修改表 3.1 重命名表名 3.2 新增列 3.3 修改列的数据类型 3.4 删除列 3.5 重命名列 四、删除表 一、创建表 我们要想在数据库中创建…

SpringMvc+阿贾克斯

0目录 1.SpringMVC 加阿贾克斯 2.分页版 1.实战 创建数据库 创建工程和pom依赖 配置web.xml和applicationContext.xml 实体类 Mapper接口方法 Mapper.xml BookService BookSeriviceImpl 控制层 测试 加入findAll.html 测试 2.分页版 控制层 PostMan测…

华为OD机试真题 Java 实现【AI面板识别】【2023 B卷 100分】,附详细解题思路

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明4、控制台输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08…

Redis旁路缓存,大KEY

Redis 旁路缓存 由于高并发原因&#xff0c;先更新数据库和先更新缓存策略都会因为延迟时间而导致数据不一致问题。 两种策略 先删除缓存&#xff0c;再更新数据库&#xff1b;先更新数据库&#xff0c;再删除缓存。 因为缓存的写入通常要远远快于数据库的写入&#xff0c;…

nginx 反向代理浅谈

前言 通常情况下&#xff0c;客户端向Web服务器发送请求&#xff0c;Web服务器响应请求并返回数据。而在反向代理中&#xff0c;客户端的请求不直接发送到Web服务器&#xff0c;而是发送到反向代理服务器。反向代理服务器会将请求转发给真实的Web服务器&#xff0c;Web服务器响…

MySQL - 报错问题解决

1、[HY000][1290] 错误信息"[HY000][1290] MySQL服务器使用了–secure-file-priv选项&#xff0c;因此无法执行此语句"表示MySQL服务器已配置启用了–secure-file-priv选项。此选项限制了LOAD DATA INFILE和SELECT INTO OUTFILE语句可以读取或写入文件系统的位置。默…

【C++】STL中stack,queue容器适配器的模拟实现(使用deque容器)

文章目录 前言一、deque的一些基本知识相比vector&#xff1a;相比list&#xff1a;3为什么选择deque作为stack和queue的底层默认容器 二、stack模拟实现三、queue的模拟实现 前言 STL标准库中stack和queue的底层结构 &#xff1a;虽然stack和queue中也可以存放元素&#xff0c…

java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法

文章目录 一、记录文件相关操作方法二、代码1.读取路径返回List\<File>2.读取路径返回List\<String>3.删除文件夹4.删除文件 一、记录文件相关操作方法 二、代码 1.读取路径返回List<File> import org.slf4j.LoggerFactory; import org.slf4j.Logger;impo…

【计算机视觉 | 图像分割】arxiv 计算机视觉关于图像分割的学术速递(7 月 26 日论文合集)

文章目录 一、分割|语义相关(7篇)1.1 Learning Transferable Object-Centric Diffeomorphic Transformations for Data Augmentation in Medical Image Segmentation1.2 Optical Flow boosts Unsupervised Localization and Segmentation1.3 Spectrum-guided Multi-granularity…