《深入理解Spring原理》 04-Spring利用 “三级缓存” 解决循环依赖

     03-IOC容器初始化之Bean注入详解   一文中详解分析了Spring是注入Bean的,以后的文章都会在该文的基础上进行知识面的扩展。废话不多说,本文的结构如下:

  1. Spring注入Bean的几种方式
  2. 什么是Bean的循环依赖
  3. Spring “三级缓存”
  4. 源码方式分析Spring是如何利用 “三级缓存” 解决Bean的循环依赖

 

Step1:Spring注入Bean的几种方式

     Spring有如下几种方式注入Bean:

  1.  构造器注入
  2.  Field属性注入
PS: 笔者在此处只列举了两种方式,因为这两种比较有代表性,其他方式不再本章范围内。感兴趣的读者可以自己研究下

   NO1:构造器注入

    如下图所示分别使用注解和xml配置方式向Spring注入了Bean。由于注入的Demo这个Bean是一个空的类,所以该Bean只有一个默认的构造器。

@Component
punlic class Demo {}

xml中定义Bean:
<bean id="demo" class="com.test.Demo"/>

  不管使用下图中哪种方式注入,最终都会调用如下所示 createBeanInstance 方法,通过源码可以看到对于Demo这样的只有构造器的Bean,SPring最终会通过确定Bean的构造器进而去进行反射创建Bean然后注入到IOC中

//创建Bean实例
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // Make sure bean class is actually resolved at this point.
    Class < ?>beanClass = resolveBeanClass(mbd, beanName);

    if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
    }

    Supplier < ?>instanceSupplier = mbd.getInstanceSupplier();
    if (instanceSupplier != null) {
        return obtainFromSupplier(instanceSupplier, beanName);
    }

    if (mbd.getFactoryMethodName() != null) {
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }

    // Shortcut when re-creating the same bean...
    boolean resolved = false;
    boolean autowireNecessary = false;
    if (args == null) {
        synchronized(mbd.constructorArgumentLock) {
            if (mbd.resolvedConstructorOrFactoryMethod != null) {
                resolved = true;
                autowireNecessary = mbd.constructorArgumentsResolved;
            }
        }
    }
    if (resolved) {
        if (autowireNecessary) {
            return autowireConstructor(beanName, mbd, null, null);
        } else {
            return instantiateBean(beanName, mbd);
        }
    }

    //确定最优构造器
    Constructor < ?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
        return autowireConstructor(beanName, mbd, ctors, args);
    }

    // 使用默认构造器进行初始化
    return instantiateBean(beanName, mbd);
}

 

NO2:Field属性注入

  如下图所示演示了Filed Bean的注入:

public class Test {
    private Demo d;

    public void setDemo(Demo demo) {this.d = demo}
}

<bean id="test" class="com.test.Test">
    <property name="d" ref="demo"/>
</bean>

  由于在xml中配置了Test类中 d 属性的引用,所以Spring在注入Test的时候,会判断类中需要注入的属性。由于在xml中指定了将demo指向了d属性,所以Spring会先注入demo,然后将demo的引用赋值给d.通过debug发现Spring最终调用了如下方法判断需要被赋值引用是否是‘可写’的,即 ph.isWritable()这个方法。该方法主要其实就是判断了一下 d 属性 是否有setter方法。拖Test类中没有setDemo()这个方法,则Spring注入会直接报错。

private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
    PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
    // 判断属性是否是‘可写’的
    if (ph == null || !ph.isWritable()) {
        if (pv.isOptional()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Ignoring optional value for property '" + tokens.actualName + "' - property not found on bean class [" + getRootClass().getName() + "]");
            }
            return;
        } else {
            throw createNotWritablePropertyException(tokens.canonicalName);
        }
    }

    Object oldValue = null;
    try {
        Object originalValue = pv.getValue();
        Object valueToApply = originalValue;
        if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
            if (pv.isConverted()) {
                valueToApply = pv.getConvertedValue();
            } else {
                if (isExtractOldValueForEditor() && ph.isReadable()) {
                    try {
                        oldValue = ph.getValue();
                    } catch(Exception ex) {
                        if (ex instanceof PrivilegedActionException) {
                            ex = ((PrivilegedActionException) ex).getException();
                        }
                        if (logger.isDebugEnabled()) {
                            logger.debug("Could not read previous value of property '" + this.nestedPath + tokens.canonicalName + "'", ex);
                        }
                    }
                }
                valueToApply = convertForProperty(tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
            }
            pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
        }
        ph.setValue(valueToApply);
    } catch(TypeMismatchException ex) {
        throw ex;
    } catch(InvocationTargetException ex) {
        PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
        if (ex.getTargetException() instanceof ClassCastException) {
            throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException());
        } else {
            Throwable cause = ex.getTargetException();
            if (cause instanceof UndeclaredThrowableException) {
                // May happen e.g. with Groovy-generated methods
                cause = cause.getCause();
            }
            throw new MethodInvocationException(propertyChangeEvent, cause);
        }
    } catch(Exception ex) {
        PropertyChangeEvent pce = new PropertyChangeEvent(getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
        throw new MethodInvocationException(pce, ex);
    }
}

 

  综上所述介绍了两种代表性的注入方式,讲解这个的目的是为了为下面要将的 Bean循环依赖做铺垫。

 

Step2: 什么是Bean的循环依赖

  如下图所示演示Java中一个对象的循环引用,当运行main方法后,直接抛出了 StackOverflow 异常。

public class A {
    public A() {new B();}
}

public class B {
    public B() {new A();}
}

public static void main(Stirng[] args) {
    new A();
}

  如下图所示演示了Spring中通过Field注入Bean方式的循环依赖问题,当启动Spring后发现可以正常运行。但是当使用 构造器注入Bean方式进行循环依赖时,Spring却抛出了异常。 通过debug源码发现,当Spring注入一个Bean时,会将Bean放入inCreationCheckExclusions 和 singletonsCurrentlyInCreation中标记该Bean正在被注入防止注入多个Bean。

所以通过构造器注入方式循环依赖时,报错原因如下:

1. Spring首先注入 AService 是首先会将该Bean的name放入inCreationCheckExclusions 和 singletonsCurrentlyInCreation

中 ,当注入时发现有一个 b属性,然后会就会到IOC中查找 Bservice 的引用

2. 若有Bservice引用则直接赋值给b属性,否则会去创建BSerivce 这个Bean,在本例中由于是IOC初始化所以会去实例化BSerivce

3. 当去实例化BService时,发现有个 a属性,然后就又会去查找 AService引用,由于第一步还没有结束,所以IOC是没有ASerivce的。然后去尝试注入AService,但是Spring注入前有一个对inCreationCheckExclusions 和 singletonsCurrentlyInCreation有一个校验,会判断这两个缓存中是否包含 ASerivce的beanName.若有则直接抛异常。

protected void beforeSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
}
public class AService {
  private BService b;

  public void setB(Bservice b) {
    this.b = b;
  }
}

public class BService {
  private AService a;

  public void setA(Aservice a) {
    this.a = a;
  }
}

<bean id="aService" class="com.ssm.TestApplication.AService">
    <property name="b" ref="bService"/>
</bean>

<bean id="bService" class="com.ssm.TestApplication.BService">
    <property name="a" ref="aService"/>
</bean>

   那为什么通过Field注入Bean方式循环依赖不会报错呢?这个完全是Spring “三级缓存”的功劳

 

Step3: Spring “三级缓存”

在Spring容器的整个声明周期中,单例Bean有且仅有一个对象。这很容易让人想到可以用缓存来加速访问。 从源码中也可以看出Spring大量运用了Cache的手段,在循环依赖问题的解决过程中甚至不惜使用了“三级缓存”,这也便是它设计的精妙之处~

三级缓存其实它更像是Spring容器工厂的内的术语,采用三级缓存模式来解决循环依赖问题,这三级缓存分别指:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	...
	// 从上至下 分表代表这“三级缓存”
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一级缓存
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存
	...
	
	/** Names of beans that are currently in creation. */
	// 这个缓存也十分重要:它表示bean创建过程中都会在里面呆着~
	// 它在Bean开始创建时放值,创建完成时会将其移出~
	private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

	/** Names of beans that have already been created at least once. */
	// 当这个Bean被创建完成后,会标记为这个 注意:这里是set集合 不会重复
	// 至少被创建了一次的  都会放进这里~~~~
	private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
}

  Spring获取IOC中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;
}
  1.    spring首先从一级缓存 singletonObjects 中获取Bean,(如何获取到则直接返回)
  2.   如果一级缓存没有命中Bean,并且要获取的Bean是正在创建中的Bean,则尝试从二级缓存中获取(如果获取到则直接return)
  3.  如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过getObject()获取。就从三级缓存singletonFactory.getObject()获取。(如果获取到了就从singletonFactories中移除,并且放进earlySingletonObjects。其实也就是从三级缓存移动(是剪切、不是复制哦~)到了二级缓存)
PS: 加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决

  

此处说一下二级缓存earlySingletonObjects它里面的数据什么时候添加什么移除???

添加:向里面添加数据只有一个地方,就是上面说的getSingleton()里从三级缓存里挪过来

移除addSingleton、addSingletonFactory、removeSingleton从语义中可以看出添加单例、添加单例工厂ObjectFactory的时候都会删除二级缓存里面对应的缓存值,是互斥的

 

Step4: 源码方式分析Spring是如何利用 “三级缓存” 解决Bean的循环依赖

下面以本文中Ste2中的field注入Bean循环依赖例子进行讲解,默认读者已经了解 Bean注入IOC的详细过程,跟兴趣的读者也可以阅读笔者的 《深入理解Spring原理》 03-IOC容器初始化之Bean注入详解

 

public class AService {
  private BService b;

  public void setB(Bservice b) {
    this.b = b;
  }
}

public class BService {
  private AService a;

  public void setA(Aservice a) {
    this.a = a;
  }
}

<bean id="aService" class="com.ssm.TestApplication.AService">
    <property name="b" ref="bService"/>
</bean>

<bean id="bService" class="com.ssm.TestApplication.BService">
    <property name="a" ref="aService"/>
</bean>

当Spring启动时首先会去注册 aService这个Bean,当流程走到 AbstractAutowireCapableBeanFactory的doCreateBean方法时:

  1. 首先会执行createBeanInstance初始化 aService这个Bean
  2. 然后获取判断 aSerivce这个Bean是否允许循环依赖,若允许则将aSerivce放入 Spring的第三级缓存 singletonFactorys 中
  3. 再往下就是解决 aSerivce 这个Bean中的 filed 属性
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final@Nullable Object[] args) throws BeanCreationException {

    
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        //1. 实例化 aService
        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;
        }
    }

    // 判断 aService是否允许循环依赖
    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");
        }
        //如果aService允许依赖,则将该Bean放入 SPring 的 第三级缓存  singletonFactorys中
        addSingletonFactory(beanName, () - >getEarlyBeanReference(beanName, mbd, bean));
    }

    
    Object exposedObject = bean;
    try {
        // 解决 aService中的属性
        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;
}

   在 AbstractAutowireCapableBeanFactory的doCreateBean方法创建 aSerivce的三步流程中,前两步是非常好理解的。那么解决循环依赖的问题,最关键的是第三步:解决 aSerivce中的filed 属性

  通过上方源码可知,解决 aSerivce中的filed 属性调用的是 populateBean(beanName, mbd, instanceWrapper); 这个方法:

 那我们就来看看这个方法做了哪些事情:

 由于aSerivce类比较简单 ,所以通过Debug下面的方法直接走到最后一步 : applyPropertyValues(beanName, mbd, bw, pvs);

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    if (bw == null) {
        if (mbd.hasPropertyValues()) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
        } else {
            return;
        }
    }

   
    boolean continueWithPropertyPopulation = true;
    // 判断Bean中是否有 InstantiationAwareBeanPostProcessor
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp: getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                    continueWithPropertyPopulation = false;
                    break;
                }
            }
        }
    }

    if (!continueWithPropertyPopulation) {
        return;
    }

    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
    // 判断是否有 Autowire
    if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

        // Add property values based on autowire by name if applicable.
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
            autowireByName(beanName, mbd, bw, newPvs);
        }

        // Add property values based on autowire by type if applicable.
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
            autowireByType(beanName, mbd, bw, newPvs);
        }

        pvs = newPvs;
    }

    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
    // 判断是否需要进行依赖检查
    boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

    if (hasInstAwareBpps || needsDepCheck) {
        if (pvs == null) {
            pvs = mbd.getPropertyValues();
        }
        PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
        if (hasInstAwareBpps) {
            for (BeanPostProcessor bp: getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                    if (pvs == null) {
                        return;
                    }
                }
            }
        }
        if (needsDepCheck) {
            checkDependencies(beanName, mbd, filteredPds, pvs);
        }
    }

    //若Bean中有属性,则进行属性填充
    if (pvs != null) {
        applyPropertyValues(beanName, mbd, bw, pvs);
    }
}

如下图所示是 applyPropertyValues 方法的源码:

 通过Debug发现流程会走到 for (PropertyValue pv : original)  中,并且走到 for循环中的else逻辑中:

String propertyName = pv.getName(); // b
Object originalValue = pv.getValue();  // BService
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);

前两步主要是获取filed的属性名称和value类型。最重要的就是地三步:解决属性Value

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
    if (pvs.isEmpty()) {
        return;
    }

    if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) { ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
    }

    MutablePropertyValues mpvs = null;
    List < PropertyValue > original;

    if (pvs instanceof MutablePropertyValues) {
        mpvs = (MutablePropertyValues) pvs;
        if (mpvs.isConverted()) {
            // Shortcut: use the pre-converted values as-is.
            try {
                bw.setPropertyValues(mpvs);
                return;
            } catch(BeansException ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", ex);
            }
        }
        original = mpvs.getPropertyValueList();
    } else {
        original = Arrays.asList(pvs.getPropertyValues());
    }

    TypeConverter converter = getCustomTypeConverter();
    if (converter == null) {
        converter = bw;
    }
    BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

    // Create a deep copy, resolving any references for values.
    List < PropertyValue > deepCopy = new ArrayList < >(original.size());
    boolean resolveNecessary = false;
    for (PropertyValue pv: original) {
        if (pv.isConverted()) {
            deepCopy.add(pv);
        } else {
            String propertyName = pv.getName(); //b
            Object originalValue = pv.getValue(); //BSerivce
            Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
            Object convertedValue = resolvedValue;
            boolean convertible = bw.isWritableProperty(propertyName) && !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
            if (convertible) {
                convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
            }
            // Possibly store converted value in merged bean definition,
            // in order to avoid re-conversion for every created bean instance.
            if (resolvedValue == originalValue) {
                if (convertible) {
                    pv.setConvertedValue(convertedValue);
                }
                deepCopy.add(pv);
            } else if (convertible && originalValue instanceof TypedStringValue && !((TypedStringValue) originalValue).isDynamic() && !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
                pv.setConvertedValue(convertedValue);
                deepCopy.add(pv);
            } else {
                resolveNecessary = true;
                deepCopy.add(new PropertyValue(pv, convertedValue));
            }
        }
    }
    if (mpvs != null && !resolveNecessary) {
        mpvs.setConverted();
    }

    // Set our (possibly massaged) deep copy.
    try {
        bw.setPropertyValues(new MutablePropertyValues(deepCopy));
    } catch(BeansException ex) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", ex);
    }
}

在 resolveValueIfNecessary 方法中,首先判断了value是否属于 运行时Bean依赖.在本例子中当然属于运行时依赖,所以该方法又直接调用了 resolveReference(argName,ref)方法 返回 aService中的属性 b  (即 BSerivce).

public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
    // We must check each value to see whether it requires a runtime reference
    // to another bean to be resolved.
    if (value instanceof RuntimeBeanReference) {
        RuntimeBeanReference ref = (RuntimeBeanReference) value;
        return resolveReference(argName, ref);
    } else if (value instanceof RuntimeBeanNameReference) {
        String refName = ((RuntimeBeanNameReference) value).getBeanName();
        refName = String.valueOf(doEvaluate(refName));
        if (!this.beanFactory.containsBean(refName)) {
            throw new BeanDefinitionStoreException("Invalid bean name '" + refName + "' in bean reference for " + argName);
        }
        return refName;
    } else if (value instanceof BeanDefinitionHolder) {
        // Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.
        BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
        return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
    } else if (value instanceof BeanDefinition) {
        // Resolve plain BeanDefinition, without contained name: use dummy name.
        BeanDefinition bd = (BeanDefinition) value;
        String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(bd);
        return resolveInnerBean(argName, innerBeanName, bd);
    } else if (value instanceof ManagedArray) {
        // May need to resolve contained runtime references.
        ManagedArray array = (ManagedArray) value;
        Class < ?>elementType = array.resolvedElementType;
        if (elementType == null) {
            String elementTypeName = array.getElementTypeName();
            if (StringUtils.hasText(elementTypeName)) {
                try {
                    elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());
                    array.resolvedElementType = elementType;
                } catch(Throwable ex) {
                    // Improve the message by showing the context.
                    throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName, "Error resolving array type for " + argName, ex);
                }
            } else {
                elementType = Object.class;
            }
        }
        return resolveManagedArray(argName, (List < ?>) value, elementType);
    } else if (value instanceof ManagedList) {
        // May need to resolve contained runtime references.
        return resolveManagedList(argName, (List < ?>) value);
    } else if (value instanceof ManagedSet) {
        // May need to resolve contained runtime references.
        return resolveManagedSet(argName, (Set < ?>) value);
    } else if (value instanceof ManagedMap) {
        // May need to resolve contained runtime references.
        return resolveManagedMap(argName, (Map < ?, ?>) value);
    } else if (value instanceof ManagedProperties) {
        Properties original = (Properties) value;
        Properties copy = new Properties();
        original.forEach((propKey, propValue) - >{
            if (propKey instanceof TypedStringValue) {
                propKey = evaluate((TypedStringValue) propKey);
            }
            if (propValue instanceof TypedStringValue) {
                propValue = evaluate((TypedStringValue) propValue);
            }
            if (propKey == null || propValue == null) {
                throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName, "Error converting Properties key/value pair for " + argName + ": resolved to null");
            }
            copy.put(propKey, propValue);
        });
        return copy;
    } else if (value instanceof TypedStringValue) {
        // Convert value to target type here.
        TypedStringValue typedStringValue = (TypedStringValue) value;
        Object valueObject = evaluate(typedStringValue);
        try {
            Class < ?>resolvedTargetType = resolveTargetType(typedStringValue);
            if (resolvedTargetType != null) {
                return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
            } else {
                return valueObject;
            }
        } catch(Throwable ex) {
            // Improve the message by showing the context.
            throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName, "Error converting typed String value for " + argName, ex);
        }
    } else if (value instanceof NullBean) {
        return null;
    } else {
        return evaluate(value);
    }
}

下面是 resolveReference方法的具体实现:

 很明显属性b不是aService的父类,所以会直接走else 逻辑。

最关键的就是else中的第一行代码 : bean = this.beanFactory.getBean(refName);

这句代码不就是 IOC直接获取bean吗?若没有获取到bean就又会执行Bean的创建流程。但是需要注意的是,此要创建的Bean是BSerivce这个bean.当创建BSerivce Bean时候,又会走到判断Bean中是否有属性,若有则填充出现的逻辑。

毫无疑问BService是有属性a的,即AService.那么最终在创建BSerivce这个Bean中又会走到 resolveReference 这个方法去解决Bservice中的a属性(即AService).然后又会去执行 this.beanFactory.getBean(refName) ,此时 refName是 aService.

当去执行 this.beanFactory.getBean(aService) 的时候,流程如下:

this.beanFactory.getBean(aService)  ---->   AbstractBeanFactory.doGetBean() ----> DefaultSingletonBeanRegistry.getSingleton()

当执行 doGetBean会先执行getSingleton到缓存中尝试获取 aService.

还记得 IOC启动时注入 AService的时候,已经将 ASerivce放到了 三级缓存中,所以填充BService中的a属性时,其实是直接从三级缓存中获取到了a属性的引用 ASerivce这个Bean。

当填充完BService中的a属性之后,将BSerivce这个bean 引用返回给 AService中的b属性。然后完成AService的创建。

当AService完成创建之后,加下来就是创建BSerivce,由于在创建AService时已经创建好了BService,所以此时会直接从缓存中返回BSerivce .进而完成了循环依赖的问题。

private Object resolveReference(Object argName, RuntimeBeanReference ref) {
    try {
        Object bean;
        String refName = ref.getBeanName();
        refName = String.valueOf(doEvaluate(refName));
        if (ref.isToParent()) {
            if (this.beanFactory.getParentBeanFactory() == null) {
                throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName, "Can't resolve reference to bean '" + refName + "' in parent factory: no parent factory available");
            }
            bean = this.beanFactory.getParentBeanFactory().getBean(refName);
        } else {
            bean = this.beanFactory.getBean(refName);
            this.beanFactory.registerDependentBean(refName, this.beanName);
        }
        if (bean instanceof NullBean) {
            bean = null;
        }
        return bean;
    } catch(BeansException ex) {
        throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName, "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
    }
}

 

综上所述,SPring解决循环依赖流程如下:

依旧以上面AB类使用属性field注入循环依赖的例子为例,对整个流程做文字步骤总结如下:

  1. 使用context.getBean(A.class),旨在获取容器内的单例A(若A不存在,就会走A这个Bean的创建流程),显然初次获取A是不存在的,因此走A的创建之路~
  2. 实例化A(注意此处仅仅是实例化),并将它放进缓存(此时A已经实例化完成,已经可以被引用了)
  3. 初始化A:@Autowired依赖注入B(此时需要去容器内获取B)
  4. 为了完成依赖注入B,会通过getBean(B)去容器内找B。但此时B在容器内不存在,就走向B的创建之路~
  5. 实例化B,并将其放入缓存。(此时B也能够被引用了)
  6. 初始化B,@Autowired依赖注入A(此时需要去容器内获取A)
  7. 此处重要:初始化B时会调用getBean(A)去容器内找到A,上面我们已经说过了此时候因为A已经实例化完成了并且放进了缓存里,所以这个时候去看缓存里是已经存在A的引用了的,所以getBean(A)能够正常返回
  8. B初始化成功(此时已经注入A成功了,已成功持有A的引用了),return(注意此处return相当于是返回最上面的getBean(B)这句代码,回到了初始化A的流程中~)。
  9. 因为B实例已经成功返回了,因此最终A也初始化成功
  10. 到此,B持有的已经是初始化完成的A,A持有的也是初始化完成的B,完美~

 

上述内容如有不妥之处,还请读者指出,共同探讨,共同进步!

@author : jackcheng1117@163.com

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值