spring

spring中的循环引用

————————————————

在博客上研究了shadow?s大神的pring源码关于循环引用的笔记,自己根据源码进行学习,在此记录一下自己的笔记。
大神的博客地址:https://blog.csdn.net/java_lyvee/article/details/101793774

首先说一下,spring在单例的情况下是默认支持循环引用的

1.spring在创建bean的时候会先创建这个bean的对象,第一次调用AbstractBeanFactory的doGetBean方法中的getSingleton方法看单例池中是否有这个bean,并且看用于记录正在创建的set集合中是否有这个对象,没有就返回继续创建对象

 * 这里是spring第一次调用getSingleton
 */
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	//首先从单例池中根据名字获取这个bean,单例池是一个Map类型
	Object singletonObject = this.singletonObjects.get(beanName);
	//判断如果为空并且有正在创建中的对象,也就是进行第二次getSingleton时候往set集合中放入了值就继续往下,否则直接返回null
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			//首先从第三个map中根据名字获取对象,earlySingletonObjects临时存放创建的对象
			singletonObject = this.earlySingletonObjects.get(beanName);
			//如果没有获取到并且是允许循环依赖的就继续
			if (singletonObject == null && allowEarlyReference) {
				//从第二个map中根据名字获取对象工厂,singletonFactories里面是工厂,拥有临时存放的对象,并且能够对对象做一些改变
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					//调用doCreateBean方法,创建对象
					singletonObject = singletonFactory.getObject();
					//把单例对象放入第三个map中临时存储
					this.earlySingletonObjects.put(beanName, singletonObject);
					//从第二个map中移除对象
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}

2.调用第二次getSingleton方法,在DefaultSingletonBeanRegistry这个类中,这时候会把对象放入一个set集合中,用于记录正在创建的对象

 *this.singletonsCurrentlyInCreation.add(beanName)这里就是放到set集合中
 */
protected void beforeSingletonCreation(String beanName) {
	if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
		throw new BeanCurrentlyInCreationException(beanName);
	}
}

3.调用AbstractAutowireCapableBeanFactory类的doCreateBean方法继续创建bean,根据容器中的allowEarlyReference属性以及是否单例,以及是否是正在创建的对象来判断是否能够临时缓存到map中

/**
					 *
					 * 此处就是创建一个bean的入口
					 * 这里调用AbstractAutowireCapableBeanFactory的doCreateBean方法
					 *
					 */
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
	/**
		 *
		 * 这里判断是否是单例,是否支持循环引用,是否正在创建中
		 *
		 */
		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");
			}
			//这里是把创建出来的对象放入map中
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			//填充属性,也就是自动注入
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			//首先判断单例池中是否有这个bean
			if (!this.singletonObjects.containsKey(beanName)) {
				//没有,就把此对象放入第二个map中临时存储
				this.singletonFactories.put(beanName, singletonFactory);
				//把此对象从第三个map中移除
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}

4.allowEarlyReference属性在spring中默认为true,默认会把对象缓存到map中供后续获得。当然这个属性spring也提供了Api来进行修改,
annotationConfigApplicationContext.setAllowCircularReferences(false)

	AnnotationConfigApplicationContext annotationConfigApplicationContext =
				new AnnotationConfigApplicationContext();
		annotationConfigApplicationContext.register(AppConfig.class);
		annotationConfigApplicationContext.setAllowCircularReferences(false); //关闭循环依赖支持

		//初始化spring容器的环境
		annotationConfigApplicationContext.refresh();

5.当创建的对象A走生命周期走到需要注入属性对象B的时候,spring就会去创建对象B,B同样与上面一样走流程,当走到需要注入属性对象A的时候,也会去调用getSingleton方法,这时候就能够从第二个map中获取到A对象,(为什么是第二个map,是因为里面是工厂,我们可以改变创建的对象来满足一些需求,比如说AOP)因为这时候在set集合中有这个对象A,注入B中后继续走完生命周期,完成bean的创建

6.B创建完成之后,容器中就有了B这个bean,这样A也能完成生命周期,完成bean的创建

解释一下三个map:

	 * 单例池map叫做singletonObjects,第一个map
		 * 存放临时对象的map叫做singletonFactories,不过这里存放的是一个工厂,可以通过一些方式来改变bean,比如Aop,第二个map
		 * 还有另一个存在临时对象的map叫做earlySingletonObjects   这里存放的是一个临时对象,第三个map

解释一下为什么要存在第二个map中,而不是作为对象直接存在第三个map中:

 *第二个map中的工厂,主要调用了以下这个方法
	 *
	 * 用AOP举例
	 * spring 实例化A,然后发现需要注入B,这时候A还没有完成代理,
	 * spring去实例化B,发现需要注入A,就去调用getSingleton,拿到没有完成代理的A
	 * 这是就有问题了,A是一个没有完成代理的对象,这跟我们需要的不一样,就错了
	 * 因此需要第二个map,也就是里面的工厂来做处理,帮我们完成AOP代理并放到第三个map中
	 * 这样我们就能拿到正确的代理的A了
	 *
	 */
	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值