Spring源码解析四之创建Bean(解决循环依赖)

首先,循环依赖是啥,用白话说,最简单的案例就是A中有B,而B中又有A,这样一个例子并不能说明什么,但是Spring容器创建Bean是有顺序的。

假设有A、B两类

public class A {
    B b;

    public B getB() {
        return b;
    }
    public void setB(B b) {
        this.b = b;
    }
}
public class B {
    A a;

    public A getA() {
        return a;
    }
    public void setA(A a) {
        this.a = a;
    }
}
<bean id = "a" class="test.v1.vo.A" scope="singleton">
    <property name="b" ref="b"></property>
</bean>

<bean id = "b" class="test.v1.vo.B" scope="singleton">
    <property name="a" ref="a"></property>
</bean>

这个时候我们调用

A a = (A)bf.getBean("car");

我们来回顾一下Spring容器获取Bean过程,当然我们只讲重点。首先因为A是单例,而Spring中有三级缓存来存储单例Bean

DefaultSingletonBeanRegistry

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

但是我们假设的大前提是A是第一次创建,所以三级缓存中都不存在该单例,由于getBean顺序是首先从三级缓存中获取单例,再到从parentBeanFactory获取,如果仍然获取不到则这时候则开始创建Bean

doGetBean

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                          @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {


    // 创建Bean.
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            try {
                return createBean(beanName, mbd, args);
            } catch (BeansException ex) {
                // Explicitly remove instance from singleton cache: It might have been put there
                // eagerly by the creation process, to allow for circular reference resolution.
                // Also remove any beans that received a temporary reference to the bean.
                destroySingleton(beanName);
                throw ex;
            }
        });
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }


}

doCreateBean 

public class BeanDefinitionTest {

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


    ....
    
    //初始化
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    
    ....

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

    ...
}

又到了创建Bean三步走,createBeanInstance、populateBean是创建Bean非常重要的两个步骤,createBeanInstance实质是调用A类的构造函数,Spring则规定只能处理Setter依赖,而构造器依赖则不行,原因很简单,解决循环依赖的思路就是不等bean创建完成就会创建将bean引用曝光,而获取bean引用必须完成构造器函数。所以我们需要关注Setter依赖,也就是属性注入这部分。

populateBean

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {

    ...
    
    int resolvedAutowireMode = mbd.getResolvedAutowireMode();
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
            //根据属性name装配
            autowireByName(beanName, mbd, bw, newPvs);
        }
        if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            //根据属性type装配
            autowireByType(beanName, mbd, bw, newPvs);
        }
        pvs = newPvs;
    }

    ...
    
    if (pvs != null) {
        applyPropertyValues(beanName, mbd, bw, pvs);
    }
}

autowireByName

protected void autowireByName(
        String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        if (containsBean(propertyName)) {

            //getBean
            Object bean = getBean(propertyName);
            pvs.add(propertyName, bean);
            registerDependentBean(propertyName, beanName);
            if (logger.isTraceEnabled()) {
                logger.trace("Added autowiring by name from bean name '" + beanName +
                        "' via property '" + propertyName + "' to bean named '" + propertyName + "'");
            }
        }
        else {
            if (logger.isTraceEnabled()) {
                logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
                        "' by name: no matching bean found");
            }
        }
    }
}

这里我们为什么只需要看autowireByName这个函数呢,因为装配的时候ByName或者ByType最终还是会调用getBean方法,所以我们这里选取装配逻辑最简单的方法。autowireByName是根据属性的名称,也就是XML配置中<property>标签中的<name>的值,最终调用getBean(String name)来获取bean进行注入。那么根据XML的配置,A需要注入属性B,也就是调用getBean("b")。

与上文一样,B也是第一次创建,容器中缓存也不存在B,B也需要像A一样从头开始创建,当B需要注入属性时,根据XML文件的配置需要注入属性A,进入autowireByName方法然后调用getBean("a")。

然而,这次getBean("a")就跟上文a的过程就有所区别了,Spring解决循环依赖的原则就是不等bean创建完成就将bean的引用暴露(缓存)。

暴露时机(doCreateBean)

public class BeanDefinitionTest {

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


    ....

    //实例化后    

    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 

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

我们可以看到这个方法的参数是ObjectFactory,为什么它并不是一个Object呢?

public interface ObjectFactory<T> {

	T getObject() throws BeansException;

}

ObjectFactory是一个接口,调用addSingletonFactory 时利用lambda表达式实现了getObject方法,但是事情并不是那么简单,实现getObject时使用了回调函数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;
}

这里回调函数利用后置处理器对bean进行改动,这里其实就是AOP切入的时机,当然这节内容跟AOP无关。但是,不管怎么说,Spring通过实现ObjectFactory的方法将bean存储到SingletonFactory(第三级缓存)。

既然缓存有a(我们需要注意 a暴露的时机是在实例化后注入属性前,所以a本身是不完整的),所以b注入属性时调用getBean("a")可以直接从缓存中取了

doGetBean

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.
    Object sharedInstance = getSingleton(beanName);

    ...
    return (T) bean;
}

 getSingleton

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

当然很明显,a最终会从三级缓存中获取到,并且调用getObject方法,并且会进入二级缓存。这里有人会说,二级缓存的缓存作用是什么,有什么用?首先我们要明白,Spring团队是怎么定义二级缓存时

/** Cache of early singleton objects: bean name to bean instance. */

private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

有人会说,“直接放入一级缓存不就得了”,或者换种方式说,“其实解决循环依赖二级缓存就够了”。可能我的水平不够,我现在也没发现二级缓存的作用在哪,但是二级缓存的存在应该跟它的定义有关(也就是存储着还处于创建状态的bean),可能解决循环依赖二级缓存足够了,但是我认为在学习他人框架源码的时候,我们应该尝试去理解别人的意境,只要不是错误,在这个角度出发,如果作者认为,我就是打算用二级缓存来存储这些未成型的bean,或许将bean的生命周期跟这些缓存挂钩,这本身不是一种错误,不要钻牛角尖。

既然getBean("a")能通过缓存返回,那么b的属性注入就能顺利完成,回顾整个过程,它本身就是递归,b的完成会返回到第一次创建a的过程,最终a也能完成属性注入。循环依赖也就解决,其实解决循环依赖的思路非常简单。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值