Spring源码分析(八)

15 篇文章 0 订阅

Spring源码分析

第七章 Spring Ioc 循环依赖


前言


一、spring 循环依赖

在这里插入图片描述

循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。注意,这⾥不是函数的循环调⽤,是对象的相互依赖关系。循环调⽤其实就是⼀个死循环,除非有终结条件。在代码层次就是这样:

@Component
public class A {
 // A中注入了B
 @Autowired
 private B b;
}
---
@Component
public class B {
 // B中注入了A
 @Autowired
 private C c;
}
---
@Component
public class C {
 // C中注入了C
 @Autowired
 private A a;
}

需要注意的是,我这边使用的是@Autowired注解的方式进行注入;
Spring中循环依赖场景有:

  1. 构造器的循环依赖(构造器注⼊)
  2. Field 属性的循环依赖(set注⼊)

其中,构造器的循环依赖问题无法解决,只能拋出BeanCurrentlyInCreationException 异常,在解决属性循环依赖时,spring采用的是提前暴露对象的方法。

  • 单例 bean 构造器参数循环依赖(无法解决)
  • prototype 原型 bean循环依赖(无法解决)

对于原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过setXxx方法产⽣循环依赖,Spring都会直接报错处理。

一、spring 循环依赖源码分析

启动测试代码,按照创建Spring Bean的流程一直点击进入到这里:

AbstractAutowireCapableBeanFactory.java

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

	// Instantiate the bean.
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	final Object bean = instanceWrapper.getWrappedInstance();
	Class<?> beanType = instanceWrapper.getWrappedClass();
	if (beanType != NullBean.class) {
		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.postProcessed = true;
		}
	}

	// 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.isTraceEnabled()) {
			logger.trace("Eagerly caching bean '" + beanName +
					"' to allow for resolving potential circular references");
		}
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}

	// Initialize the bean instance.
	Object exposedObject = bean;
	try {
		populateBean(beanName, mbd, instanceWrapper);
		exposedObject = initializeBean(beanName, exposedObject, mbd);
	}
	catch (Throwable ex) {
		if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
			throw (BeanCreationException) ex;
		}
		else {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Initialization of bean failed", 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 " +
							"'getBeanNamesOfType' 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;
}

对于这一段代码,我们在上一章已经分析了一部分,这一章,我们主要关注这一段代码:

Object earlySingletonReference = getSingleton(beanName, false);

这段代码实际调用的是:

public Object getSingleton(String beanName) {
    return getSingleton(beanName, true);
}

getSingleton(beanName, true)这个方法实际上就是到缓存中尝试去获取Bean,整个缓存分为三级

  1. singletonObjects,一级缓存,存储的是所有创建好了的单例Bean
  2. earlySingletonObjects,完成实例化,但是还未进行属性注入及初始化的对象
  3. singletonFactories,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象

因为A是第一次被创建,所以不管哪个缓存中必然都是没有的,因此会进入getSingleton的另外一个重载方法getSingleton(beanName, singletonFactory)。


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) {
 
            // ....
            // 省略异常处理及日志
            // ....
 
            // 在单例对象创建前先做一个标记
            // 将beanName放入到singletonsCurrentlyInCreation这个集合中
            // 标志着这个单例Bean正在创建
            // 如果同一个单例Bean多次被创建,这里会抛出异常
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 上游传入的lambda在这里会被执行,调用createBean方法创建一个Bean后返回
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            // ...
            // 省略catch异常处理
            // ...
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                // 创建完成后将对应的beanName从singletonsCurrentlyInCreation移除
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                // 添加到一级缓存singletonObjects中
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

这个方法就是用来创建Bean的,通过createBean方法返回的Bean最终被放到了一级缓存,也就是单例池中。
了解完这里,我们再回到刚刚AbstractAutowireCapableBeanFactory#doCreateBean方法中,关注下这段代码:

// 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.isTraceEnabled()) {
		logger.trace("Eagerly caching bean '" + beanName +
				"' to allow for resolving potential circular references");
	}
	// 加入三级缓存
	addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

addSingletonFactory这个方法是将刚刚实例化的Bean放入三级缓存当中;我们继续向下,查看这个方法:

DefaultSingletonBeanRegistry.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);
		}
	}
}

我们来说明一下:

this.singletonFactories.put(beanName, singletonFactory);

这段代码主要的作用是添加到三级缓存中,我们可以看到三级缓存里,keybeanNamevalue 就是这个匿名类的返回值,对象工厂 singletonFactory
这里只是添加了一个工厂,通过这个工厂(ObjectFactory)的getObject方法可以得到一个对象,而这个对象实际上就是通过getEarlyBeanReference这个方法创建的,那么,什么时候会去调用这个工厂的getObject方法呢?这个时候就要到创建B的流程了。
当A完成了实例化并添加进了三级缓存后,就要开始为A进行属性注入了,在注入时发现A依赖了B,那么这个时候Spring又会去getBean(b),然后反射调用setter方法完成属性注入。
因为B需要注入A,所以在创建B的时候,又会去调用getBean(a),这个时候就又回到之前的流程了,但是不同的是,之前的getBean是为了创建Bean,而此时再调用getBean不是为了创建了,而是要从缓存中获取,因为之前A在实例化后已经将其放入了三级缓存singletonFactories中,实际上就是这样:
在这里插入图片描述
然后我们来看下getEarlyBeanReference这个方法:

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;
}

它实际上就是调用了后置处理器的getEarlyBeanReference,而真正实现了这个方法的后置处理器只有一个,就是通过@EnableAspectJAutoProxy注解导入的AnnotationAwareAspectJAutoProxyCreator
在开启AOP的情况下,那么就是调用到org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreatorgetEarlyBeanReference方法:

@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
	Object cacheKey = getCacheKey(bean.getClass(), beanName);
	this.earlyProxyReferences.put(cacheKey, bean);
    // 如果需要代理,返回一个代理对象,不需要代理,直接返回当前传入的这个bean对象
	return wrapIfNecessary(bean, beanName, cacheKey);
}

也就是说,如果我们对A进行了AOP代理的话,那么此时getEarlyBeanReference将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建后的对象。

总结

  1. ”Spring是如何解决的循环依赖?“

答:Spring通过三级缓存解决了循环依赖; 其中一级缓存为单例池(singletonObjects)
二级缓存为早期曝光对象earlySingletonObjects 三级缓存为早期曝光对象工厂(singletonFactories)。
当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。
当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取,第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。
紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

  1. 为什么要使用三级缓存呢?二级缓存能解决循环依赖吗?

答:如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理。

文章参考:面试必杀技,讲一讲Spring中的循环依赖

系列连接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值