该问题由上一章引出,上一章跟踪了spring实例化bean的整个过程。 中间出现了疑似用于解决bean循环依赖的问题,这个问题在面试中考官会经常问,能完美的答出来是个不错的加分项。
即循环引用,即两个或两个以上的bean相互引入。 如 A --> B --> C --> A。 类似代码结构:
public class A {
private B b;
}
public class B {
private C c;
}
public class C {
private C a;
}
从类示例能看的出来。是A依赖B, B依赖C,然后C又反过来依赖A。这种依赖关系其实是个死循环,除非有终结条件。
在spring中存在循环依赖的场景有:
1)构造器的循环依赖
2)field属性的循环依赖
其中构造器的循环依赖无解,容器检测到此种情况会抛出BeanCurrentlyInCreationException异常。原因是构造器方式注入时要求先注入目标bean再实例化。当递归到实例化C实例时,发现无法注入A实例。
而第二种field属性的循环依赖可以解决,spring采用的是提前暴露对象的方式。
spring容器是在bean创建的时候给bean打上标签,如果递归回来发现bean正在创建中时,就说明存在循环依赖了。
从图中可知,是先实例化bean,再设置对象属性。所以spring解决field方式的循环依赖时具备了可行性。
spring实例化A后会将对象放到一个Map中,并且spring提供了获取该实例化bean的引用方法(虽然此时的bean还未设置好属性,但已经可以引用了)。以之前的A,B,C三个类为例,当spring依次实例化了A,B,C三个类的实例后,会开始设置他们的实例属性,因为A依赖B,所以设置A类中B属性时,就会从Map中去获取引用,这样就不会存在循环引用问题。
它的理论依据是上面提的:实例化和设置实例属性可以分开。实例化后就可暴露引用。
spring实例化singleton bean主要分为三步:
注:后面代码跟踪时还能看到
createBeanInstance:实例化,即通过构造方法实例化对象
populateBean: 填充实例属性
initializeBean: 执行初始化方法和Bean后置处理器
我们要解决循环依赖问题就是在第一和第二阶段。对于sington来说,在容器的整个生命周期中有且仅有一个对象,所以很自然会想到应该缓存起来供后面复用。spring为了解决循环依赖问题,使用了三级缓存,如下:
在创建Bean的时候,首先想到的是从一级缓存(singletonObjects)中获取,该缓存是真正存实例化bean的地方,其他两个都是临时的。
如果获取不到,并且对象正在创建中,就再从二级缓存(earlySingletonObjects)中获取。如果还是获取不到,再从三级缓存(singletonFactories)中获取。如果获取到了则从三级中移除,转移到二级中。
等完全初始化完成了,再从二级缓存移到一级缓存中。
上面从整体上描述了解决循环依赖的原理,本节从代码层面跟踪容器解决循环依赖的具体实现,加深印象
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) {
// 1. 实例化bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 意思很明确:生成实例后,填充属性和执行初始化前做缓存,用于解决循环依赖
// 判断是否需要提前缓存bean是这几个条件:
// 1)本身是singleton; 2) 容器允许bean循环引用(默认允许)3)当前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");
}
// 添加到SingletonFactories三级缓存,下面会展开
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 2. 填充属性
populateBean(beanName, mbd, instanceWrapper);
// 3. 执行初始化和bean后置处理器
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);
}
}
// 如果是提前暴露bean引用的情况,先从一级缓存(singletonObjects)中取,再从earlySingletonObjects中取,最后从singletonFactories中取。 细节下面再展开
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;
}
当bean刚实例化出来后,容器会判断该bean是否需要提前缓存,判断条件:
当满足时,执行:
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);
}
}
}
还是假设 A -> B -> C -> A的依赖情况,当从实例化后填充属性需要获取A实例时,又回调到以下方法(省略了当前不关心的部分):
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 提起检查该bean是否已经存在,此时就是会从三个缓存中找,跟进去看看是不是
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 + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// ignore...
return (T) bean;
}
在该方法中执行了三层缓存的查询逻辑,跟之前的猜想一致
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 先从一级缓存中取
Object singletonObject = this.singletonObjects.get(beanName);
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;
}
本节系统讨论了spring中如何解决循环依赖问题,从理论到源码都过了一遍,希望能在面试中遇到此类问题时从容一些。