《Spring源码深度解析》五

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

第五章:bean的加载

对应代码:

《Spring源码深度解析》五


流程:AbstractBeanFacotry

@Override
public Object getBean(String name) throws BeansException {
    return doGetBean(name, nullnullfalse);
}
//重点代码
protected <T> doGetBean(final String name, @Nullable final Class<T> requiredType,
        @Nullable final Object[] args, boolean typeCheckOnly)
 throws BeansException 
{
    //
    final String beanName = transformedBeanName(name);
    //5.2
    Object sharedInstance = getSingleton(beanName);
    //5.3
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    //5.4getSingleton 5.5createBean
    sharedInstance = getSingleton(beanName, () -> {
                    try {
                        return createBean(beanName, mbd, args);
                    }

    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);


    return createBean(beanName, mbd, args);

    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);

    return (T) bean;
  1. 转换对应beanName
    这里传入的参数可能是别名,也可能是FactoryBean ,所以需要进行一系列的解析,这些解析内容包括如下内容:
    a.去除FactoryBean 的修饰符,也就是如果name="&aa”,那么会首先去除&而使name=”aa” 。
    b.取指定alias 所表示的最终beanName,例如别名A 指向名称为B 的bean 则返回B;
    若别名A指向别名B ,别名B又指向名称为C 的bean,则返回C 。

  2. 尝试从缓存中加载单例
    单例在Spring 的同一个容器内只会被创建一次,后续再获取bean ,就直接从单例缓存中获取了。
    当然这里也只是尝试加载,首先尝试从缓存中加载,如果加载不成功则再次尝试从singletonFactories 中加载。
    因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,在Spring中创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory 提早曝光加入到缓存中, 一旦下一个bean 创建时候需要依赖上一个bean 则直接使用ObjectFactory

  3. bean 的实例化
    如果从缓存中得到了bean 的原始状态,则需要对bean 进行实例化。
    缓存中记录的只是最原始的bean 状态, 井不一定是我们最终想要的bean 。
    举个例子,假如我们需要对工厂bean 进行处理,那么这里得到的其实是工厂bean的初始状态,但是我们真正需要的是工厂bean中定义的factory-method方法中返回的bean ,而getObjectForBeanlnstance就
    是完成这个工作的

  4. 原型模式的依赖检查
    只有在单例情况下才会尝试解决循环依赖,如果存在A中有B 的属性, B 中有A 的属性,那么当依赖注入的时候,就会产生当A 还未创建完的时候因为对于B 的创建再次返回创建A,造成循环依赖
    也就是情况: isPrototyp巳CurrentlyinCreation(beanName)判断true 。

  5. 检j则parentBeanFactory
    从代码上看,如果缓存没有数据的话直接转到父类工厂上去加载了,这是为什么呢?
    这里有一个很重要的判断条件: 
    parentBeanFactory !=null && !containsBeanDefinition(beanName), parentBeanFactory != null 
    parentBeanFactory 如果为空, 则其他一切都是浮云,
    containsBeanDefinition(beanName)则是在检测如果当前加载的XML配置文件中不包含beanName 所对应的配置,就只能到parentBeanFactory 去尝试下了,然后再去递归的调用getBean方法。

  6. 将存储XML 配置文件的GernericBeanDefinition 转换为RootBeanDefinition
    因为从XML 配置文件中读取到的bean 信息是存储在G巳rnericBeanDefinition 中的,但是所
    有的bean 后续处理都是针对于RootBeanDefinition 的,所以这里需要进行一个转换,转换的同时
    如果父类bean 不为空的话,则会一并合并父类的属性。

  7. 寻找依赖
    因为bean的初始化过程中很可能会用到某些属性,而某些属性很可能是动态配置的,并且配置成依赖于其他的bean ,那么这个时候就有必要先加载依赖的bean ,所以,在Spring的加载顺寻中,在初始化某一个bean 的时候首先会初始化这个bean 所对应的依赖。

  8. 针对不同的scope 进行bean的创建(最重要)
    我们都知道,在Spring 中存在着不同的scope ,其中默认的是singleton ,但是还有些其他的配置诸如prototype 、request 之类的。在这个步骤中, Spring 会根据不同的配置进行不同的初始化策略。

  9. 类型转换
    程序到这里返回bean 后已经基本结束了,通常对该方法的调用参数requiredType 是为空的
    但是可能会存在这样的情况,返回的bean 其实是个String ,但是requiredType却传人Integer类型,那么这时候本步骤就会起作用了,它的功能是将返回的bean转换为requiredType 所指定的类型。
    当然, String 转换为Integer 是最简单的一种转换,在Spring 中提供了各种各样的转换器,用户也可以自己扩展转换器来满足需求。

在具体分析各个步骤之前,先了解下FactoryBean的用法

5.1:FactoryBean的使用

用户可以通过实现org.Springframework.bean.factory.FactoryBean接口定制实例化bean的逻辑
getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,即被FactoryBean#getObject()所代理

好处:使用编码方式替代配置,否则需要在中提供大量的配置信息,灵活性受限

package org.springframework.beans.factory;

import org.springframework.lang.Nullable;

public interface FactoryBean<T{
    /**
     * 返回由FactoryBean创建的Bean实例
     * 如果isSingleton返回true,则该实例会放到Spring容器中的单实例缓存池中
     * @return
     * @throws Exception
     */

    @Nullable
    getObject() throws Exception;

    /**
     * 返回FactoryBean创建的bean类型
     * @return
     */

    @Nullable
    Class<?> getObjectType();

    /**
     * 返回由FactoryBean创建的bean实例的作用域使singleton还是prototype
     * @return
     */

    default boolean isSingleton() {
        return true;
    }
}

5.2:缓存中获取单例bean,即流程2

Object sharedInstance = getSingleton(beanName);

单例在Spring 的同一个容器内只会被创建一次,后续再获取bean,就直接从单例缓存中获取了。
当然这里也只是尝试加载,首先尝试从缓存中加载,如果加载不成功则再次尝试从singletonFactories 中加载。
因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,在Spring中创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory 提早曝光加入到缓存中, 一旦下一个bean 创建时候需要依赖上一个bean则直接使用ObjectFactory

@Override
@Nullable
public Object getSingleton(String beanName) {
    //true允许早期依赖
    return getSingleton(beanName, true);
}

@Nullable
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);
            //如果此bean正在加载则不处理
            if (singletonObject == null && allowEarlyReference) {
                //当某些方法需要捉前初始化的时候则会调用addSingletonFactory方法将对应的
                //ObjectFactory初始化策略存储在singletonFactories
                ObjectFactory<?> singletonFactory =                              this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //调用预先设定的getObject方法创建bean
                    singletonObject = singletonFactory.getObject();
                    //记录在缓存中,earlySingletonObjects和singletonFactories互斥
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

几个map如下:

singletonObjects :用于保存BeanName 和创建bean实例之间的关系, beanName :beanInstance

singletonFactories :用于保存BeanName 和创建bean 的工厂之间的关系, beanName:ObjectFactory

earlySingletonObjects :
也是保存BeanName 和创建bean 实例之间的关系
与singletonObjects 的不同之处在于,当一个单例bean 被放到这里面后,那么当bean 还在创建过程中,就可以通过getBean方法获取到了,其目的是用来检测循环引用

registeredSingletons :用来保存当前所有巳注册的bean

5.3:从bean的实例中获取对象

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    ...
    object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    ...
}

根据这个方法得到bean的实例后,判断其是否为FactoryBean类型的bean,若是,则调用该bean对应的FactoryBean实例中的getObject()方法作为返回值

getObjectForBeanInstance的作用:

《Spring源码深度解析》五


流程:

  1. 对FactoryBean 正确性的验证。

  2. 对非FactoryBean 不做任何处理。

  3. 对bean 进行转换。

  4. 将从Factory 中解析bean 的工作委托给getObjectFromFactoryBean

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess){
    ...
    object = doGetObjectFromFactoryBean(factory, beanName);
    //调用ObjectFactory的后处理器,尽可能保证所有bean初始化后都会调用注册的BeanPostProcessor和postPorcessAfterInitialization方法进行处理。实际开发中可以使用此特性设计业务逻辑
    object = postProcessObjectFromFactoryBean(object, beanName);
    ...
}

返回的bean如果是单例的,则必须要保证全局唯一,可以使用缓存来提高性能

doGetObjectFromFactoryBean为真正发挥作用的方法
即如果bean 声明为FactoryBean 类型,则当提取bean时提取的并不是FactoryBean ,而是FactoryBean中对应的getObject方法返回的bean

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) throws BeanCreationException{
    ...
    //直接调用getObject方法
    object = factory.getObject(); 
}

5.4:获取单例

5.2中,如果缓存中不存在已经记载的单例bean,则需要从头开始加载bean,即getSingleton

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory){
    ...
    //加载单例前记录加载状态,目的是为了可以对循环依赖进行检测(把正要创建的bean记录在缓存中)
    beforeSingletonCreation(beanName);
    //初始化实例化bean
    singletonObject = singletonFactory.getObject();
    //移除缓存中对该bean的正在加载状态的记录
    afterSingletonCreation(beanName);
    //加入缓存,并删除加载bean过程中所记录的各种辅助状态
    addSingleton(beanName, singletonObject);
    ...
}

使用了回调方法,在单例创建前后可以做一些事情

5.5:准备创建bean

流程:

I. 根据设置的class属性或者根据className来解析Class

2 对override属性进行标记及验证

3.应用初始化前的后处理器,解析指定bean 是否存在初始化前的短路操作。

4.创建bean

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException{
    ...
    //5.5.1 验证及准备覆盖的方法
    mbdToUse.prepareMethodOverrides();
    //5.5.2 Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
    Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
    //5.5.4 创建bean
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    ...
}

5.5.1:处理override属性

public void prepareMethodOverrides() throws BeanDefinitionValidationException {
    ...
    for (MethodOverride mo : overrides) {
        prepareMethodOverride(mo);
    }
    ...
}

protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
    //获取对应类中对应方法名的个数
    int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
    if (count == 0) {
        throw ...
    }
    else if (count == 1) {
        //标记MethodOverride暂未被覆盖,避免参数类型检查的开销
        mo.setOverloaded(false);
    }
}

5.5.2:实例化的前置处理

对后处理器中的所有InstantiationAwareBeanPostProcessors类型的后处理器调用PostProcessorsBeforeInstantiation和BeanPostProcessorsAfterInitialization

实例化前的后处理器应用:applyBeanPostProcessorsBeforeInstantiation

实例化后的后处理器应用:applyBeanPostProcessorsAfterInitialization

@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    Object bean = null;
    //如果尚未被解析
    if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
        // Make sure bean class is actually resolved at this point.
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            Class<?> targetType = determineTargetType(beanName, mbd);
            if (targetType != null) {
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                if (bean != null) {
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
        }
        mbd.beforeInstantiationResolved = (bean != null);
    }
    return bean;
}

5.5.3:循环依赖

1.构造器循环依赖:
通过构造器注入构成得循环依赖无法解决,只能抛异常。

Spring容器维护一个“当前创建bean池”;
bean标识符在创建过程中将一直保持在这个池中;
因此如果在创建bean过程中发现自己已经在“当前创建bean 池” 里时,将抛出BeanCurrentlylnCreationException 异常表示循环依赖;
而对于创建完毕的bean将从“ 当前创建bean 池”中清除掉。

例子:如果创建A需要B,创建B需要C,创建C需要A,则创建A时A进入池,进入创建B的过程;B、C依次类推,则B、C都在池中;所有bean做好准备开始创建A时,发现A在“当前创建bean池”中,所以报错;如果A不需要其他bean,则直接创建,并从池中移除

2.setter循环依赖

解决:通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter 注入)的bean来完成的,只能解决单例作用域的bean循环依赖
通过提前暴露一个单例工厂方法,从而使其他bean能引用到该bean

3.prototype范围的依赖处理

无法完成依赖注入,因为Spring容器不进行缓存“prototype”作用域的bean ,因此无法提前暴露一个创建中的bean

对于singleton作用域bean,可以通过setAllowCircularReferences(false);来禁用循环引用

5.5.4:创建bean

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException
{
    //此方法很复杂,最复杂的是循环依赖(5.5.3)的处理
    //完成这步后,就完成了bean的创建
}

流程:

《Spring源码深度解析》五


处理循环依赖:
在B 中创建依赖A时通过ObjectFactory提供的实例化方法来中断A中的属性填充, 使B中持有的A仅仅是刚刚初始化并没有填充任何属性的A ,而这初始化A的步骤还是在最开始创建A的时候进行的,但是因为A与B中的A所表示的属性地址是一样的,所以在A中创建好的属性填充自然可以通过B中的A获取,这样就解决了循环依赖的问题。

《Spring源码深度解析》五


填充属性:

《Spring源码深度解析》五

原文始发于微信公众号(不止于Java):《Spring源码深度解析》五