Spring源码剖析之循环依赖

13 篇文章 0 订阅
10 篇文章 1 订阅

什么是循环依赖

循环依赖其实就是循环引用,两个或两个以上的Bean相互依赖,相互引用,例如A依赖B,B依赖于A,如果不加以处理,Bean A,Bean B 都无法创建成功。因为创建A的时候发现依赖B,于是去创建B,创建B的时候依赖A,这样整个SpringBean创建周期无法完成。

Spring中循环依赖的场景

依赖注入方式

  • 构造器注入导致循环依赖
  • set注入
  • 注解注入

构造器注入导致的循环依赖,无法被解决,因为创建A需要对象B,B又需要A,连基本的反射创建对象都创建不了。实际上就是Bean B ,Bean A 实例化不了。只能拋出 BeanCurrentlyInCreationException 异常

refresh方法

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 刷新前预处理
			prepareRefresh();

			// 获取之前创建的BeanFactory,默认实现DefaultListableBeanFactory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// BeanFactory的预准备⼯作(BeanFactory进⾏⼀些设置,设置类加载器,环境等等。
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);
				
				//  实例化并调用默认配置,和自定义的BeanFactoryPostProcessor,实现核心配置类解析,包组件扫描封装BeanDefinition.
				invokeBeanFactoryPostProcessors(beanFactory);

				// 注册默认配置的和自定义的BeanPostProcessor , 实现bean创建赋值之后 init 方法前后调用
				registerBeanPostProcessors(beanFactory);

				// 初始化MessageSource组件,实现 国际化功能,消息绑定,消息解析
				initMessageSource();

				// 初始化应用事件派发器
				initApplicationEventMulticaster();

				// 子类重写
				onRefresh();

				// 注册实现了ApplicationListener接⼝的监听器
				registerListeners();

				// 实例化剩下的所有单例bean
				finishBeanFactoryInitialization(beanFactory);

				// 发布事件,交由其它组件触发对应响应,例如springMVC初始化
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

循环依赖源码剖析

定义两个循环依赖的Bean A,B

在这里插入图片描述

在这里插入图片描述

测试类

在这里插入图片描述

1、由于我们之前分析过SpringBean的创建流程,因此直接定位到 实例化当前BeanDefinitionMap中的所有单例BeanDefintion

可以看到A,B已经被封装成BeanDefinition

在这里插入图片描述

注意:finishBeanFactoryInitialization(beanFactory);

在这里插入图片描述
2、目前正要实例化Bean A
在这里插入图片描述

默认通过无参构造反射实例化当前Bean A

在这里插入图片描述
获取无参构造器,获取实例化对象
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3、主动的缓存单例Bean A,以便解决循环依赖问题,默认放入三级缓存池SingletonFactorys,注意当前Bean A 只是一个空对象
在这里插入图片描述

4、对当前Bean A进行属性赋值

在这里插入图片描述

5、获取默认,和自定义的BeanPostProcessor,找到AutowireAnnotationBeanPostProcessor,实现依赖注入元素处理,当前Bean A,依赖于Bean B,那么通过他去实例化Bean B 实现依赖注入

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

6、实例化Bean B,将Bean B也放入三级缓存singletonFactorys;
在这里插入图片描述
在这里插入图片描述

7、同样对Bean B实现依赖注入,通过AutowiredAnnotationBeanPostProcessor

在这里插入图片描述
8、从IOC容器中,获取Bean B 所依赖的元素Bean A,获取位置应为三级缓存SingletonFactorys,因为singletonObjects是没有A实例的,一旦从三级缓存中获取了Bean A那么,Bean A 就会剪切到二级缓存(重点!!!!)
在这里插入图片描述

在这里插入图片描述

9、Bean B 实现依赖注入 A之后,继续完成SpringBean的创建周期,例如 调用XXXAware对应的set方法 ,调用 Bean B
init 方法,在init方法前后调用BeanPostProcessor。最终Bean B 成为了SpringBean B,放入了一级缓存SingletonObejcts中,移除二三级缓存中的Bean B

在这里插入图片描述

在这里插入图片描述

10、此时轮到Bean A 继续进行Bean属性赋值,既然一级缓存池中,已经存在了SpringBean B,那么将其注入到Bean A 当中完成依赖注入
在这里插入图片描述

11、Bean A 依赖注入后,初始化后(注意:经过后置处理器的Bean 可能是代理对象)。将二级缓存池中的Bean A取出,此时两个Bean A 引用默认指向同一个对象
在这里插入图片描述

在这里插入图片描述

// earlySingletonExposure:如果你的bean允许被早期暴露出去 也就是说可以被循环引用  那这里就会进行检查
		// 此段代码非常重要~~~~~但大多数人都忽略了它
		if (earlySingletonExposure) {
			// 此时一级缓存肯定还没数据,但是呢此时候二级缓存earlySingletonObjects也没数据
			//注意,注意:第二参数为false  表示不会再去三级缓存里查了~~~

			// 此处非常巧妙的一点:::因为上面各式各样的实例化、初始化的后置处理器都执行了,如果你在上面执行了这一句
			//  ((ConfigurableListableBeanFactory)this.beanFactory).registerSingleton(beanName, bean);
			// 那么此处得到的earlySingletonReference 的引用最终会是你手动放进去的Bean最终返回,完美的实现了"偷天换日" 特别适合中间件的设计
			// 我们知道,执行完此doCreateBean后执行addSingleton()  其实就是把自己再添加一次  **再一次强调,完美实现偷天换日**
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
			
				// 这个意思是如果经过了initializeBean()后,exposedObject还是木有变,那就可以大胆放心的返回了
				// initializeBean会调用后置处理器,这个时候可以生成一个代理对象,那这个时候它哥俩就不会相等了 走else去判断吧
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				} 

				// allowRawInjectionDespiteWrapping这个值默认是false
				// hasDependentBean:若它有依赖的bean 那就需要继续校验了~~~(若没有依赖的 就放过它~)
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					// 拿到它所依赖的Bean们~~~~ 下面会遍历一个一个的去看~~
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					
					// 一个个检查它所以Bean
					// removeSingletonIfCreatedForTypeCheckOnly这个放见下面  在AbstractBeanFactory里面
					// 简单的说,它如果判断到该dependentBean并没有在创建中的了的情况下,那就把它从所有缓存中移除~~~  并且返回true
					// 否则(比如确实在创建中) 那就返回false 进入我们的if里面~  表示所谓的真正依赖
					//(解释:就是真的需要依赖它先实例化,才能实例化自己的依赖)
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}

					// 若存在真正依赖,那就报错(不要等到内存移除你才报错,那是非常不友好的) 
					// 这个异常是BeanCurrentlyInCreationException,报错日志也稍微留意一下,方便定位错误~~~~
					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 " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

12、Bean A 完成创建流程,将其放入一级缓存池SingletonObjects中,移除二级,三级缓存池中的Bean,由此Bean A,转换为了SpringBean A。

在这里插入图片描述

最终结果:发现通过注解没有出现循环依赖Bean不能创建的问题

在这里插入图片描述

总结:

在这里插入图片描述

注意:依赖注入交由Bean属性赋值阶段的AutowireAnnotationBeanPostProcessor处理。

在Bean初始化阶段的时候通过BeanPostProcessor实现动态代理对对象进行代理。如果是循环依赖对象,那么该 代理对象此时,应该在二级缓存earlySingletonObjects中

SingletonObejcts: 一级缓存,存放完整的SpringBean,代理Bean,Bean最后的归宿

EarlySingletonObjects: 二级缓存,存放代理Bean, 普通Bean

SingletonFactorys: 三级缓存,存放原始Bean,即反射调用构造方法创建的Bean

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白鸽呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值