【spring】spring如何解决循环依赖问题,跟着我debug走一遍

本人最近想学习一下spring的源码,无奈spring源码实在是庞大,想要全面学习有点困难,不如逐个点攻破。在网上随便翻面试题的时候偶然看到这个问题,想着就从这开始吧。刚开始学spring的时候,网课上的老师就带着一行一行的往里看,那看的是一个头昏眼花,直犯困。直到现在,看了几年java,想要了解每一行代码都做了什么,还是非常晦涩的,感觉只有去问作者了。我现在也看开了,不再深究每行代码都做了什么,很多方法就看个输入输出,大概知道做了啥事就行了,毕竟也不需要我们写一个spring出来。我感觉spring最厉害的点就在于他的可扩展性,通过各种回调方法,监听器扩展,可以灵活设计各种中间件。spring中的设计模式运用也是相当巧妙,如果对设计模式不够了解的话,想要看懂spring还真是有点难。没办法了,只能硬着头皮上了。

我发现用chatgpt学习源码也挺不错,不过3.5版本的还是有点问题,我发现他喜欢自己编源码,生成源码和实际的总会有点出入。不过我们可以灵活点,抓住某个点问他这行代码做了什么,给个注释啥的。感觉如果gpt4或者5的话应该就是神器了,不过既然ai这么懂源码,还要我们干嘛,哎~~。

spring解决循环依赖老生常谈的话题了,网上资料一大堆,大家都知道是用了三级缓存。一级缓存存储实例化、属性注入、初始化完成的bean,二级缓存存储实例化完成但还没有属性注入的bean,三级缓存存储提前暴露对象ObjectFactory。如果发生循环依赖,某个bean注入依赖的时候,可以调用三级缓存中的ObjectFactory.getObject方法获取实例化完成但是还没有属性注入的半成品bean。

说实话,循环依赖这里很绕,因为这里用了递归。spring在创建一个bean的时候,都会先调用getBean方法,尝试获取一下,如果获取不到就调用createBean方法创建。然后在populateBean属性注入的时候,同样还会调用getBean方法,尝试获取一下,获取不到就创建 。属性注入的时候如果属性类中又有需要依赖注入的bean,还会调用getBean,就这么一直递归下去。递归是要有递归终止条件的,递归的终止条件就是getBean时三级缓存中有值,并调用三级缓存中ObjectFactory的getObject方法拿到了半成品bean。获取到了半成品bean,此时的递归停止,开始返回。

首先先准备测试代码:

@Component
public class Demo1{
	@Autowired
	private Demo2 demo2;
}

@Component
public class Demo2{
	@Autowired
	private Demo1 demo1;
}

根据下面的调用链,来到getBean的doGetBean方法中。spring的源码里有个习惯,某项功能的方法,方法名前面加了do才是真正执行逻辑的方法。比如真正获取bean的方法是doGetBean由getBean调用doGetBean;真正创建bean的方法是doCreateBean由createBean方法调用doCreateBean。

SpringApplication#run(TestApplication.class, args) ---->
run(new Class<?>[] {primarySource}, args) ---->
new SpringApplication(primarySources).run(args) ---->
refreshContext(context) ----> refreshContext(context) ---->
((AbstractApplicationContext) applicationContext).refresh() ---->
finishBeanFactoryInitialization(beanFactory) ---->
beanFactory.preInstantiateSingletions() ---->
DefaultListableBeanFactory#preInstantiateSingletons() ---->
getBean(name) ---->
doGetBean(name, null, null, false)

根据上面的流程,进入AbstractBeanFactory类中的doGetBean方法。注意啊,在我们的示例代码中,Demo1和Demo2相互依赖,所以在debug测试的时候,下面的doGetBean()方法会进入4次,我这里标记一下。我每次只展示会用到的代码,其他代码先省略,下次进来的时候,如果用到了其他代码,会再展示。(我的源码都是粘贴自互联网,里面的注释也是粘贴的)

第一次进入doGetBean(),此get的beanName是demo1

protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
		throws BeansException {     
    
    // 处理别名BeanName、处理带&符的工厂BeanName
	final String beanName = transformedBeanName(name);
	Object bean;  

	// 先尝试从缓存中获取Bean实例,这个位置就是三级缓存解决循环依赖的方法
	Object sharedInstance = getSingleton(beanName);   
	
	// 后面的逻辑先省略,用到的时候再粘贴...
	
    // 返回 Bean
	return (T) bean;
}

getSingleton(beanName)方法中可以看到了三个缓存了:
singletonObjects:一级缓存
earlySingletonObjects: 二级缓存
singletonFactories:三级缓存

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// 1.首先在单例bean集合中检查当前beanName对应的bean是不是已经被创建过了,如果被创建过了直接取出
    Object singletonObject = this.singletonObjects.get(beanName);
    // 2.如果当前beanName对应的bean还没有被创建过 && 当前的bean是否正在创建(首次false)
    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);
}

isSingletonCurrentlyInCreation()用来判断当前beanName是否正在创建中。
此时的beanName是demo1,由于demo1还没有被创建为bean,isSingletonCurrentlyInCreation()会返回false,getSingleton()直接返回null。

再次回到doGetBean()方法中。继续向下执行,来到mbd.isSingleton的if代码块中,调用getSingleton()的重载方法。注意,在getSingleton参数中,传入了一个ObjectFactory对象,该对象的getObject方法会调用createBean()方法。说明这次的getSingleton不只是get,还会create。

protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
		throws BeansException {     
    
    // 处理别名BeanName、处理带&符的工厂BeanName
	final String beanName = transformedBeanName(name);
	Object bean;  

	// 先尝试从缓存中获取Bean实例,这个位置就是三级缓存解决循环依赖的方法
	Object sharedInstance = getSingleton(beanName);   

	if (sharedInstance != null && args == null) {        
        // 1. 如果 sharedInstance 是普通的 Bean 实例,则下面的方法会直接返回
        // 2. 如果 sharedInstance 是工厂Bean类型,则需要获取 getObject 方法,可以参考关于 FactoryBean 的实现类 
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}
	else {
			// ... 省略其他代码
			
			// 创建单例 bean 实例
			if (mbd.isSingleton()) {    
			    // 把 beanName 和 new ObjectFactory 匿名内部类传入回调
				sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
					@Override
					public Object getObject() throws BeansException {
						try {    
                            // 创建 bean
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// 创建失败则销毁
							destroySingleton(beanName);
							throw ex;
						}
					}
				});
				bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
			}      
	}    

    // 返回 Bean
	return (T) bean;
}

进入到getSingleton()的重载方法中。

  • 首先到singletonObjects集合中检查“demo1”是否存在,显然是不存在的
  • 将“demo1”加入到singletonsCurrentlyInCreation集合中,表示“demo1”正在被创建
  • 回调ObjectFactory对象的getObject方法,getObject方法中将会调用createBean方法
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) {
            // ... 省略部分代码
            
            // 将bean添加到singletonsCurrentlyInCreation中,前面的isSingletonCurrentlyInCreation方法就是从这里面操作的集合中检测是否正在创建。
            // 由于这个beanName被加入到了singletonsCurrentlyInCreation集合中,所以下次又遇到这个beanName,isSingletonCurrentlyInCreation方法就会返回true。
            beforeSingletonCreation(beanName);
            
            // ... 省略部分代码
            
            try {
                // 这里调用该方法参数中传入的lambda表达式,执行createBean()方法
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            // 后面的方法先省略 ...
    }
}

createBean方法我们就不看了,直接来到doCreateBean方法

  • 首先根据beanDefinition创建一个beanWrapper对象,这是beanDefinition和bean的中间产物
  • 如果“demo1”是单例模式,支持循环依赖,且正在被创建,则将“demo1”提前暴露
  • 对“demo1”进行依赖注入
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException {

	// 这里先根据RootBeanDefinition创建一个beanWrapper,根据这个名字可以推断,这是bean的装饰对象。反正这里只是createBean的第一步,将beanDefinition转为BeanWrapper。
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	
	// 提前暴露,用于解决循环依赖问题
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
	if (earlySingletonExposure) {
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}
	
	Object exposedObject = bean;
	// 属性赋值
	populateBean(beanName, mbd, instanceWrapper);
	// ... 省略部分代码
	return exposedObject;
}

addSingletonFactory()中实现了对“demo1”的提前暴露。

看到这里,我们可以总结一下之前的步骤:

  1. 首先一个类在变成一个bean之前,会先用beanDefinition对象来描述一个类
  2. 一个bean在创建之前,首先会调用getSingleton()方法,getSingleton()方法中先根据beanName到一级缓存中看是否存在,第一次来肯定是不存在的,所以返回null。
  3. 第一次没有获取到bean后,对beanDefiniton做merge操作。(与本次主题无关,略过)
  4. 调用重载的另一个getSingleton()方法,getSingleton()方法中再次检查一级缓存中是否已经存在该bean,第一次来肯定是不存在的,所以依旧返回null。
  5. 调用doCreateBean()方法,来创建bean。
  6. 创建bean之前,先把beanDefinition转为BeanWrapper对象。(与本次主题无关,略过)
  7. 如果当前是单例模式创建bean,且允许循环依赖,引入到addSingletonFactory方法中。首先检查当前beanName是否存在一级缓存中,第一次肯定是不存在的。如果不存在则将当前正在创建的bean的ObjectFactory对象放入到三级缓存中(提前暴露),将当前beanName从二级缓存中删除。

进入到addSingletonFactory方法:

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
	synchronized (this.singletonObjects) {
		//如果一级缓存里没有
		if (!this.singletonObjects.containsKey(beanName)) {
			//放入三级缓存
			this.singletonFactories.put(beanName, singletonFactory);
			//从二级缓存移除
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

addSingletonFactory做了一个重要的操作,就是将对象提前暴露。
再次回到doCreateBean()中。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException {

	// 这里先根据RootBeanDefinition创建一个beanWrapper,根据这个名字可以推断,这是bean的装饰对象。反正这里只是createBean的第一步,将beanDefinition转为BeanWrapper。
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	
	// 提前暴露,用于解决循环依赖问题
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
	if (earlySingletonExposure) {
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}
	
	Object exposedObject = bean;
	// 属性赋值
	populateBean(beanName, mbd, instanceWrapper);
	// ... 省略部分代码
	return exposedObject;
}

populateBean(beanName, mbd, instanceWrapper)方法实现对“demo1”对象的依赖注入。这行代码里面做的操作很复杂,但是大体就是为bean中标记了@Autowired的变量进行赋值。

Demo1中通过@Autowired注解引入了Demo2,所以通过populateBean方法进行依赖注入的时候,又会调用getBean ----> doGetBean方法尝试获取并创建demo2。

第二次进入doGetBean(),此get的beanName是demo2

我们再次回到doGetBean()方法中,此时再次进入到getSingleton()中,注意此时getBean的已经不是demo1了,而是它内部的实例变量demo2。由于demo2也是初次创建,所以走到这里和demo1走到这里的流程是一样的。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// 1.首先在单例bean集合中检查当前beanName对应的bean是不是已经被创建过了,如果被创建过了直接取出
    Object singletonObject = this.singletonObjects.get(beanName);
    // 2.如果当前beanName对应的bean还没有被创建过 && 当前的bean是否正在创建(首次false)
    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);
}

由于此刻的demo1和demo2都是spring初次尝试获取并创建,所以第一次demo1进入doGetBean和第二次demo2进入doGetBean的流程都是一样的。

if (mbd.isSingleton()) {
	sharedInstance = getSingleton(beanName, () -> {
		try {
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			destroySingleton(beanName);
			throw ex;
		}
	});
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

这里与demo1来到的也没什么两样,直接到doCreateBean()中,再次展示一下doCreateBean()方法。

在执行到populateBean方法之前,demo2也会提前暴露,生成ObjectFactory对象放入到三级缓存中。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException {

	// 这里先根据RootBeanDefinition创建一个beanWrapper,根据这个名字可以推断,这是bean的装饰对象。反正这里只是createBean的第一步,将beanDefinition转为BeanWrapper。
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	
	// 提前暴露,用于解决循环依赖问题
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
	if (earlySingletonExposure) {
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}
	
	Object exposedObject = bean;
	// 属性赋值
	populateBean(beanName, mbd, instanceWrapper);
	// ... 省略部分代码
	return exposedObject;
}

由于demo2中通过@Autowired注解引入了demo1,所以在populateBean()方法中,会通过getBean()方法尝试获取demo1的bean。所以,下面就是第三次进入doGetBean()方法

第三次进入doGetBean(),此get的beanName是demo1

这次是在处理demo2的依赖注入问题时需要获取demo1的bean调用的doGetBean方法。此时的getSingleton方法已有不同。因为isSingletonCurrentlyInCreation不再返回false,而是返回true了。

  • 首先检查一级缓存中是否存在“demo1”,目前还不存在
  • 如果当前beanName对应的bean还没有被创建过 && 当前的bean是否正在创建(首次本次为true)
  • 其次检查二级缓存中是否存在“demo1”,目前还不存在
  • 再检查三级缓存中是否存在“demo1”,发现存在,取出“demo1”的ObjectFactory对象,并调用ObjectFactory对象的getObject方法,得到Demo1对象。
  • 将得到的Demo1对象加入到二级缓存,并删除三级缓存中的“demo1”
  • 返回bean,半成品,此时的demo1还没有完成属性注入,所以demo1中的demo2还是null
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// 1.首先在单例bean集合中检查当前beanName对应的bean是不是已经被创建过了,如果被创建过了直接取出
    Object singletonObject = this.singletonObjects.get(beanName);
    // 2.如果当前beanName对应的bean还没有被创建过 && 当前的bean是否正在创建(首次本次为true)
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
        	// 检查二级缓存中是否有该beanName,有就直接返回,第一还没有,当前Demo1的beanName只是提前暴露,放到了三级缓存中
            singletonObject = this.earlySingletonObjects.get(beanName);
            // 二级缓存中没有
            if (singletonObject == null && allowEarlyReference) {
            	// 二级缓存中没有且允许循环依赖,则检查三级缓存中是否存在当前beanName(Demo1存在)
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                	// 调用提前暴露的Demo1的ObjectFactory对象的getObject方法
                    singletonObject = singletonFactory.getObject();
                    // 调用三级缓存中存储的Demo1的ObjectFactory对象的getObject方法,拿到了Demo1的实例化对象
                    // 不过此时Demo1实例化对象中的Demo2属性还是为null
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

由于demo1此时在doGetBean的getSingleton方法中返回了不为null的对象,就不会再走createBean了。

那么这时,第二轮中,Demo2的populateBean方法已经执行成功了,Demo2已经依赖注入完成,拿到了demo1。第二轮的createBean方法完成,退出后回到第二轮的doGetBean的getSingleton方法中。createBean是在getSingleton的singletonFactory.getObject()方法中被回调了,继续往后看到addSingleton方法。

  • addSingleton方法将已经实例化、属性注入、初始化好的demo2从二级缓存中删除并放入到了一级缓存中。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(beanName, "Bean name must not be null");
	// 因为创建过程中需要操作 singletonObjects。所以需要加锁
	synchronized (this.singletonObjects) {
		// 再次尝试获取bean,判断bean是否已经加载。如果加载直接返回。
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null) {
			// ...
			// 做一些bean创建前的准备工作: 记录beanName 正在加载的状态(添加到 singletonsCurrentlyInCreation 缓存中),若bean已经正在加载,则抛出异常。为了解决循环引用的问题
			beforeSingletonCreation(beanName);
			// ...
			try {
				// 通过回调方式获取bean实例。
				singletonObject = singletonFactory.getObject();
				newSingleton = true;
			}
			// ...
			if (newSingleton) {
				// 加入到缓存中,并删除加载bean过程中所记录的各种辅助状态
				addSingleton(beanName, singletonObject);
			}
		}
		return singletonObject;
	}
}

...
// 主要还是对几个缓存map的操作
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);
	}
}

demo2的bean已经完成创建成功了,所以第一轮中demo1的poplulateBean()调用的getBean()返回了demo2的成品bean。demo1的populateBean调用结束,依赖注入完成,完成初始化方法后,demo1的bean也完成了创建。

结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值