一文详解 Spring Boot 创建 Bean 的过程

在讨论 Spring 时使用的 Bean 指的是托管在 Spring 容器(或称 IoC 容器)中的 Java 类对象。 Bean 的作用范围分为 Singleton、Prototype 两种(spring-web 又增加了 request\session 等)。 默认情况下,Bean 的作用范围是 Singleton。 在 IoC 容器启动时,默认会创建、初始化 Singleton Bean,这个过程被称之为 “eager registration of singletons”。 Spring 创建 Singleton Bean 的过程可以分为三个阶段:

  1. 实例化阶段,AbstractAutowireCapableBeanFactory#createBeanInstance,这个阶段会调用构造器、工厂方法等来完成 Bean 的实例化。
  2. 属性设置阶段,AbstractAutowireCapableBeanFactory#populateBean,这个阶段会设置依赖。
  3. 初始化阶段,AbstractAutowireCapableBeanFactory#initializeBean,如果设置了 Bean 实例的初始化方法,会在这个阶段调用。

接下来,我将逐个分析上述三个阶段,并与大家一块研究下 Spring 中每个过程的实现源码。

01-createBeanInstance

首先,创建 Bean 的过程在 AbstractAutowireCapableBeanFactory#createBeanInstance 方法中实现。 它的主要作用是对 Singleton Bean 进行实例化,例如通过调用类的构造器、工厂方法等。 实例化后的 Bean 通过一个包装类 BeanWrapper 对象返回。 下面,我们来看下这部分的源码实现:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    /**
    * 一般来说,这里得到的就是要创建的 Bean 的类型,或者是它的工厂类类型
    */
    // Make sure bean class is actually resolved at this point.
    Class<?> beanClass = resolveBeanClass(mbd, beanName);   

    /**
    * 如果有 supplier,则使用 supplier 获得实例
    */
    Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
    if (instanceSupplier != null) {
        return obtainFromSupplier(instanceSupplier, beanName);
    }

    /**
    * 根据工厂类、工厂方法获得实例
    * 最终交由 ConstructorResolver#instantiateUsingFactoryMethod 方法实现
    */
    if (mbd.getFactoryMethodName() != null) {
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }

    /**
    * 如果前面两种方式都没能创建实例,则需要根据要创建 Bean 的构造器来实例化
    * args 是要使用的构造器参数
    * 如果 args == null,说明要使用默认够构造器
    */
    // 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) {
            /**
            * 对构造方法中需要自动注入的进行处理
            * 最终交由 ConstructorResolver#autowireConstructor 处理
            */
            return autowireConstructor(beanName, mbd, null, null);
        }
        else {
            /**
            * 使用默认够构造器
            * 根据实例化策略(SimpleInstantiationStrategy 或 CglibSubclassingInstantiationStrategy)来创建实例
            * 然后返回 BeanWrapper 对象
            */
            return instantiateBean(beanName, mbd);
        }
    }

    /**
    * 这里应对的是 args != null 的情况
    * 与前面一样,也是分为两种情况考虑:
    * 1. 对于需要 autowiring 的情况,由 ConstructorResolver#autowireConstructor 处理
    * 2. 其他情况,直接调用构造器创建对象
    */
    // Candidate constructors for autowiring?
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
            mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
        return autowireConstructor(beanName, mbd, ctors, args);
    }

    // Preferred constructors for default construction?
    ctors = mbd.getPreferredConstructors();
    if (ctors != null) {
        return autowireConstructor(beanName, mbd, ctors, null);
    }

    // No special handling: simply use no-arg constructor.
    return instantiateBean(beanName, mbd);
}
复制代码

在上述源码实现中,有几点需要特别注意:

  1. Supplier 是 Java 1.8 版本之后提供的函数式接口,它只定义了一个 get 方法,用来获取结果。 在 AbstractBeanDefinition 中定义了一个 instanceSupplier,通过这个回调,能够创建特定 Bean 的实例。 需要特别提醒的是,通过 instanceSupplier 创建 Bean 实例是从 Spring 5.0 开始支持的功能。 而且,它的优先级要高于构造器、工厂方法。
  2. 如果没有提供 Supplier,则会尝试通过工厂类、工厂方法或类构造器来创建 Bean 实例。 针对使用无参构造器的情况,直接根据实例化策略(例如基于 CGLIB),创建实例 BeanUtils#instantiateClass -> Constructor#newInstance。 针对使用工厂类、工厂方法或带参构造器的情况,由 Spring 内部的一个辅助类 ConstructorResolver 来实现。
    • instantiateUsingFactoryMethod,通过工厂方法来实例化类。工厂方法可能是静态类方法,也可能是工厂类对象。
    • autowireConstructor,从容器中自动装配对应的构造器参数。

完成实例化后,Spring 会对 Bean (BeanWrapper) 实例进行属性填充。 这部分代码在 AbstractAutowireCapableBeanFactory#populateBean 中实现。 接下来,让我们一起学习下这部分源码。

02-populateBean

AbstractAutowireCapableBeanFactory#populateBean 方法的主要作用是,根据 BeanDefinition 中的属性值来设置(填充)BeanWrapper 中的 Bean 实例:

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

    /**
    * 当使用 XML 文件作为配置文件时,<bean> 标签中的 <property> 中的内容,会设置到 BeanDefinition 的 PropertyValues 中
    * 当使用 @Configuration 类文件作为配置时,pvs 一般是空值
    */
    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

    /**
    * 这里的代码主要是检查 bw 中的引用类型(即依赖的其他 Bean)
    * 并且,从容器中获取(容器中不存在时会触发创建)这些 Bean,设置到 newPvs 中
    * 最终,applyPropertyValues 中,根据 newPvs 的值,设置 bw 中的属性值
    * 根据使用自动注入类型不同,区分为按类型注入、按 Bean 名称注入两种方式
    * 需要注意的是,这里并不会真正的修改 bw 中 Bean 的属性值
    */
    int resolvedAutowireMode = mbd.getResolvedAutowireMode();
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
        // Add property values based on autowire by name if applicable.
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
            /**
            * 根据 bean name 注入依赖,后面会讲到
            */
            autowireByName(beanName, mbd, bw, newPvs);
        }
        // Add property values based on autowire by type if applicable.
        if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            /**
            * 与根据 bean name 注入类似
            */
            autowireByType(beanName, mbd, bw, newPvs);
        }
        pvs = newPvs;
    }

    /**
    * 这里会回调 InstantiationAwareBeanPostProcessor 中的 postProcessProperties
    */
    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
    boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

    PropertyDescriptor[] filteredPds = null;
    if (hasInstAwareBpps) {
        if (pvs == null) {
            pvs = mbd.getPropertyValues();
        }
        for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
            /**
            * 当使用 AnnotationConfigApplicationContext 时,一个关键的 InstantiationAwareBeanPostProcessor 实现是 AutowiredAnnotationBeanPostProcessor
            * 后者会处理 bw 包装的 Bean 类型中的 @Autowired、@Value、@Inject 注解的 Field、Method,并收集到 InjectionMetadata
            * 之后,会调用 InjectionMetadata#inject,完成依赖对象(值、其他 Bean)的注入
            */
            PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
                if (filteredPds == null) {
                    filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                }
                /**
                * 从 5.1 版本起被废弃,推荐使用同接口中的 postProcessProperties 
                */
                pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                if (pvsToUse == null) {
                    return;
                }
            }
            pvs = pvsToUse;
        }
    }
    /**
    * 执行依赖检查,确认是否所有的属性(除那些被特别排除的外)都被设置
    * 若有未设置的,会抛异常 UnsatisfiedDependencyException
    */
    if (needsDepCheck) {
        if (filteredPds == null) {
            filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
        }
        checkDependencies(beanName, mbd, filteredPds, pvs);
    }
    /**
    * 这里把 pvs 中的值设置到 bw 中的 Bean 实例中
    */
    if (pvs != null) {
        applyPropertyValues(beanName, mbd, bw, pvs);
    }
}
复制代码

上述源码实现中,有几点需要特别注意:

  1. autowireByName 方法,它的源码如下。它会遍历所有未满足的属性,然后尝试从容器中获取这些引用,因此会出发其他 Bean 的加载。
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        // 容器中有特定的 Bean
        if (containsBean(propertyName)) {
            Object bean = getBean(propertyName);
            // 获取(可能会出发 Bean 创建)对应 Bean,然后放入 pvs 中,等待后续真正设置到 bean 中属性里
            pvs.add(propertyName, bean);
            registerDependentBean(propertyName, beanName);
        }
        else {
        }
    }
复制代码

这里的 unsatisfiedNonSimpleProperties 方法,会从 bw 中获取内部 bean 实例的所有未满足的、非简单类型(简单类型指基本类型、enum 等)的属性值。 例如:

public class Person {
    private String name;
    private String sex;
    // 省略 getter\setter
}
复制代码

它对应的 unsatisfiedNonSimpleProperties 方法放回值就包括 name、sex、class 等。 需要特别注意的是,如果没有 getter 和 setter 方法,这个属性不会出现在返回值列表中。 2. 在 Spring 中,有很多形如 xxxPostProcessors 的接口定义。 这是 Spring 提供的一种生命周期回调机制,允许使用者通过提供 xxxPostProcessor 具体实现的方式,参与到 Spring 的流程中,来实现特定的处理逻辑。 我个人也认为这是一种非常灵活的定制化机制,在开发自己的系统时,可以借鉴、参考 Spring 的这种方式。

当所有必须的属性填充完毕后,Bean 实例已经算是可用的了。 但是,对于一些比较复杂的 Bean,可能需要执行一些初始化步骤,才能真正对外提供服务。 Spring 会在下一个阶段,调用 Bean 的初始化方法。

03-initializeBean

调用 Bean 的初始化方法在 AbstractAutowireCapableBeanFactory#initializeBean 中实现。

/**
* bean 就是前面两步创建好的 Bean 实例
*/
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    
    /**
    * 如果 Bean 实现了 Aware 接口,调用它们定义的方法:
    * 1. BeanNameAware#setBeanName
    * 2. BeanClassLoaderAware#setBeanClassLoader
    * 3. BeanFactoryAware#setBeanFactory
    */
    invokeAwareMethods(beanName, bean);

    /**
    * 对于应用程序定义的 Bean,调用 BeanPostProcessor#postProcessBeforeInitialization 方法
    * 这个方法就是常被提到的 Bean 生命周期回调之一
    * 这里用到的也是前面提到的 PostProcessor 模式
    */
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {  // synthetic 指不是由应用本身定义的,例如由容器定义的
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    try {
        /**
        * 调用 Bean 的初始化方法
        * 对于 XML 配置文件来说,就是 <bean> 标签中指定的 init-method 方法
        * 对于 Java 类配置来说,@Bean 注解中的 initMethod 指定的方法
        * 对于实现了 InitializingBean 接口的 Bean 来说,调用 afterPropertiesSet 方法
        * 
        */
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {  }
    /**
    * 对于应用程序定义的 Bean,调用 BeanPostProcessor#applyBeanPostProcessorsAfterInitialization 方法
    * 这个方法就是常被提到的 Bean 生命周期回调之一
    */
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}
复制代码

到这里为止,Spring 创建 Bean 的过程基本就结束了。

猜你喜欢

转载自blog.csdn.net/ww2651071028/article/details/130525003