关于Spring的BeanDefinitionBuilder

2022年 4月 14日 104点热度 0人点赞

file

Introduction

使用 BeanDefinition 就可以手动创建 bean 了.

DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) this.applicationContext.getBeanFactory();

// we can take initiative to inject bean
for(int i = 0; i < this.type.size(); ++i) {
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(Demo.class);
    builder.addPropertyValue("type", this.type.get(i));
    builder.addPropertyValue("endpoint", this.endpoint);
    builder.addPropertyValue("clientId", this.clientId);
    beanFactory.registerBeanDefinition(/* bean name here */, builder.getRawBeanDefinition());
}

Bean 的生命周期

在深入 BeanDefinitionBuilder 之前, 先得了解一下 Spring 中 Bean 的生命周期.

Spring Bean 生命周期概览

Spring Bean 的完整生命周期从创建 Spring 容器开始, 直到最终 Spring 容器销毁 Bean. (那什么 Spring 的容器会销毁 bean 呢? Spring 的容器如何关闭?)

  1. 实例化 BeanFactoryPostProcessor 实现类
  2. 执行 BeanFactoryPostProcessorpostProcessBeanFactory 方法
  3. 实例化 BeanPostProcesssor 实现类
  4. 实例化 InstantiationAwareBeanPostProcessorAdapter 实现类
  5. 执行 InstantiationAwareBeanPostProcessorpostProcessBeforeInstantiation 方法
  6. 执行 Bean 的构造器
  7. 执行 InstantiationAwareBeanPostProcessorpostProcessPropertyValues 方法
  8. Bean 注入属性
  9. 调用 BeanNameWaresetBeanName 方法
  10. 调用 BeanFactoryAwaresetBeanFactory 方法
  11. 执行 BeanPostProcessorpostProcessBeforeInitialization 方法
  12. 调用 InitializingBeanafterPropertiesSet 方法
  13. 调用 <bean>init-method 属性指定的方法
  14. 执行 BeanPostProcessorpostProcessAfterInitialization 方法
  15. 执行 InstantiationAwareBeanPostPorcessorpostProcessAfterInitialization 方法
  16. 容器初始化成功, 执行正常调用后, 下面销毁容器
  17. 调用 DiposibleBeandestroy 方法
  18. 调用 <bean>destroy-method 属性指定的方法

若容器注册了以上各种接口, 程序那么将会按照以上的流程进行. 下面将仔细讲解各接口作用.

BeanDefinition 解析

以注解类变成 Spring Bean 为例, Spring 会扫描指定包下面的 Java 类, 将其变成 beanDefinition 对象, 然后根据 beanDefinition 来创建 bean.

/**
 * BeanDefinition 是对 bean 实例的描述, 其中包含属性值, 构造参数, 以及更多具体实现的信息.
 * 这就是一个最小接口, 它的主要用途就是允许 BeanFactoryPostProcessor,
 * 比如 PropertyPlaceholderConfigurer
 * to introspect 和修改属性值以及其他的 bean 元数据.
 *
 * @see ConfigurableListableBeanFactory#getBeanDefinition
 * @see org.springframework.beans.factory.support.RootBeanDefinition
 * @see org.springframework.beans.factory.support.ChildBeanDefinition
 */
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

    /**
     * 这个是标准的单例域的标识 "singleton".
     * 要注意的是 bean 工厂的扩展可能支持更多的域类型.
     * @see #setScope
     */
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;

    /**
     * 这个是标准的原型域的标识 "prototype".
     * 要注意的是 bean 工厂的扩展可能支持更多的域类型.
     * @see #setScope
     */
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

    /**
     * 这个 Role 表示这个 BeanDefinition 是应用的一个主要组成.
     * 与之相对应的是用户自定义的 bean.
     */
    int ROLE_APPLICATION = 0;

    /**
     * 这个 Role 类型表示这个 BeanDefinition 是作为一个更大配置支持部分,
     * 比较典型的就是更外层的 ComponentDefinition.
     * 支持类型的 beans 在寻找特定类型的 ComponentDefinition 很重要,
     * 但是从整个应用的配置来看则不是那么重要.
     */
    int ROLE_SUPPORT = 1;

    /**
     * 这个类型的 bean 表示这个 BeanDefinition 提供了完整的背景角色, 并且和终端用户没有关系.
     * 常用于表示注册的 bean 完全就是内部的 ComponentDefinition 一部分.
     */
    int ROLE_INFRASTRUCTURE = 2;

    // 可修改的属性

    /**
     * 如果有的话, 用于设置这个 bean definition 的父 definition.
     */
    void setParentName(String parentName);

    /**
     * 如果有的话, 则返回这个 bean definition 的父 definition.
     */
    String getParentName();

    /**
     * 指定这个 bean definition 的 bean 的 class name.
     * 这个 class name 可以在 bean facotry 的后处理中被修改,
     * 一般是用它的解析后的变种来替换原始的 class name.
     * @see #setParentName
     * @see #setFactoryBeanName
     * @see #setFactoryMethodName
     */
    void setBeanClassName(String beanClassName);

    /**
     * 返回这个 bean definition 当前的 bean class name.
     * 要注意的是这个不一定是运行时使用的实际的 class name,
     * 比如 child definition 会重写或者继承
     * 它父 definition 的 class name.
     * Also, this may just be the class that a factory method is called on, or it may
     * even be empty in case of a factory bean reference that a method is called on.
     * Hence, do <i>not</i> consider this to be the definitive bean type at runtime but
     * rather only use it for parsing purposes at the individual bean definition level.
     * @see #getParentName()
     * @see #getFactoryBeanName()
     * @see #getFactoryMethodName()
     */
    String getBeanClassName();

    /**
     * Override the target scope of this bean, specifying a new scope name.
     * @see #SCOPE_SINGLETON
     * @see #SCOPE_PROTOTYPE
     */
    void setScope(String scope);

    /**
     * 返回这个 bean 当前的域类型, 如果还未知的话就返回 null.
     */
    String getScope();

    /**
     * 设置这个是否应该懒加载初始化.
     * 如果这个属性为 false, 那么这个 bean 会在 bean factory 启用时立即进行初始化.
     */
    void setLazyInit(boolean lazyInit);

    /**
     * 返回这个 bean 是否应该懒加载, 即不是已启动就马上初始化这个类.
     * 这个属性只对单例的 bean 有效.
     */
    boolean isLazyInit();

    /**
     * 设置这个 bean 初始化所依赖的 bean 的名字, bean 工厂将会保证这些被依赖的 bean 被首先初始化.
     */
    void setDependsOn(String... dependsOn);

    /**
     * 返回这个 bean 所依赖的 bean 的名字.
     */
    String[] getDependsOn();

    /**
     * 设置这个 bean 是否能够作为候选者注入到其他的 bean 中.
     * 注意到如果设置了这个值, 则只能影响通过类型注入的.
     * 并不会影响通过名字显式引用, 即使没有被标记为 autowired candidate 也会被解析.
     * 结果就是, 如果名字匹配的话也会被注入.
     */
    void setAutowireCandidate(boolean autowireCandidate);

    /**
     * 返回这个 bean 是否是作为 autowired candidate 注入到其他的 bean 中.
     */
    boolean isAutowireCandidate();

    /**
     * 设置这个 bean 是否是同类型 bean 中首先被注入的.
     * 如果对于多个匹配的 bean 中的一个 bean, 这个值是 true 的话, 那么它将作为 tie-breaker.
     */
    void setPrimary(boolean primary);

    /**
     * 返回这个 bean 是否是首先被注入的.
     */
    boolean isPrimary();

    /**
     * 如果有的话, 则可以指定要使用的 factory bean.
     * 指定的工厂方法要调用的 bean 的名称.
     * @see #setFactoryMethodName
     */
    void setFactoryBeanName(String factoryBeanName);

    /**
     * 如果有的话则返回工厂 bean 的名称.
     */
    String getFactoryBeanName();

    /**
     * 如果有的话可以指定一个工厂方法.
     * 这个方法可以通过传入构造器参数而被调用, 如果没有指定则可以不传入参数.
     * 这个方法会在指定的 factory bean 上被调用, 如果有的话, 否则就是本地 bean class 的静态方法.
     * @see #setFactoryBeanName
     * @see #setBeanClassName
     */
    void setFactoryMethodName(String factoryMethodName);

    /**
     * 如果有的话则返回工厂方法.
     */
    String getFactoryMethodName();

    /**
     * Return the constructor argument values for this bean.
     * <p>The returned instance can be modified during bean factory post-processing.
     * @return the ConstructorArgumentValues object (never {@code null})
     */
    ConstructorArgumentValues getConstructorArgumentValues();

    /**
     * 返回可以给这个 bean 赋值的属性值.
     * 即返回的实例可以在 bean factory 的后处理中被修改, 返回的值总不是空的.
     */
    MutablePropertyValues getPropertyValues();

    // 一下是只读的属性

    /**
     * 返回的这个 bean 是否是单例的, 即每次返回的都是同一个 bean 实例.
     * @see #SCOPE_SINGLETON
     */
    boolean isSingleton();

    /**
     * 返回这个 bean 的域是否是 Prototype 的, 即每个实例都是不相关的.
     * @since 3.0
     * @see #SCOPE_PROTOTYPE
     */
    boolean isPrototype();

    /**
     * 返回这个 bean 是否是抽象的, 即不可实例化的.
     */
    boolean isAbstract();

    /**
     * 返回这个 bean definition 的 role.
     * @see #ROLE_APPLICATION
     * @see #ROLE_SUPPORT
     * @see #ROLE_INFRASTRUCTURE
     */
    int getRole();

    /**
     * 返回 bean definition 可读性更高的描述.
     */
    String getDescription();

    /**
     * 返回这个 bean definition 的资源描述, 以便在报错时显示上下文.
     */
    String getResourceDescription();

    /**
     * 返回原始的 BeanDefinition, 如果没有的话则返回 null.
     * 允许解析被装饰的 bean definition, 如果有的话.
     * 注意这个方法返回的是 immediate originator.
     * 通过迭代 originator chain 可以找到用户定义的 BeanDefinition.
     */
    BeanDefinition getOriginatingBeanDefinition();

}

上面的代码就是 beanDefinition 所包含的内容, 看到这些属性如果对 Spring 有所了解都应该知道几个:

  1. 如 lazyInit 懒加载, 在 Spring 中有一个 @Lazy 注解, 用来标识其这个 bean 是否为懒加载的
  2. scope 属性, 相应的也有 @scope 注解来标识这个 bean 其作用域, 是单例还是多例
  3. beanClass 属性, 在对象实例化时, 会根据 beanClass 来创建对象
  4. autowireMode 注入模型这个属性, 这个属性用于记录使用怎样的注入模型, 注入模型常用的有根据名称和根据类型

autowireMode 虽然看似我们没有使用到过, 但是知道这个对于查看 Spring 和其他框架整合的时候很有帮助, 如果我们在 beanDefinition 指定其根据类型进行属性注入, 那么在创建这个 bean 时, 会将其 beanClass 中的所有属性都拿到, 然后排除掉基本属性 (如类型 String, Double, Boolean, Object), 然后对于剩下的进行属性注入, 记住一点, 在 beanDefinition 指定其根据类型进行属性注入, 即使不在属性上面使用 @Autowired 注解也会对其进行属性注入. 在我们写注解类的时候为什么不使用 @Autowired 时, 其属性就注入不进来呢? 那是因为注解类在变成 beanDefinition 时, 其注入类型是不注入, 所以此时只有使用 @Autowired 注解进行标记的属性, 才会完成依赖注入.

说了这么多, 总之大家要记住 Spring 会根据 beanDefinition 来完成 bean 的创建, 为什么不直接使用对象的 class 对象来创建 bean 呢? 因为在 class 对象仅仅能描述一个对象的创建, 它不足以用来描述一个 Spring bean, 而对于是否为懒加载, 是否是首要的, 初始化方法是哪个, 销毁方法是哪个, 这个 Spring 中特有的属性在 class 对象中并没有, 所有 Spring 就定义了 beanDefinition 来完成 bean 的创建.

ApplicationContext 类图

file

在接口中有这么一个方法:

org.springframework.context.ConfigurableApplicationContext#refresh

它的实现是在这个类里面:

org.springframework.context.support.AbstractApplicationContext#refresh

这个接口的注释如下:

/**
 * 加载或者刷新配置的持久化表示,
 * 这些配置可能来自于基于 Java 的配置, XML 文件, properties 文件, 关系型数据库或者其他格式.
 *
 * 由于这是一个在启动时运行的方法, 如果它失败的话则应该销毁掉已经创建的单例 bean, 避免悬空资源.
 * 换句话说, 在调用这个方法之后, 要么生成所有的单例的 bean, 要么不应该有单例的 bean.
 * @throws BeansException if the bean factory could not be initialized
 * @throws IllegalStateException if already initialized and multiple refresh
 * attempts are not supported
 */
void refresh() throws BeansException, IllegalStateException;

以下是这个接口方法的实现:

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 准备用于刷新的上下文
        prepareRefresh();

        // 告诉子类去刷新内部的 bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 准备在这个 context 中使用的 bean factory.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // 销毁已经创建的单例的 bean, 避免悬挂资源.
            destroyBeans();

            // 重置 'active' 标识
            cancelRefresh(ex);

            // 将异常传递给调用者
            throw ex;
        }

        finally {
            // 重置 Spring 核心中的通用的内省缓存, 因为对于单例 bean 来说, 它们已经不需要了
            resetCommonCaches();
        }
    }
}

执行流程分析:

我们简单的分析下代码的步骤:

  1. 初始化前的准备工作, 例如对系统属性或者环境变量进行准备及验证.
    在某种情况下项目的使用需要读取某些系统变量, 而这个变量的设置很可能会影响着系统的正确性, 那么 ClassPathXmlApplicationContext 为我们提供的这个准备函数就显得非常必要, 它可以在 Spring 启动的时候提前对必须的环境变量进行存在性验证.
  2. 初始化 BeanFactory, 并进行 XML 文件读取.
    之前提到 ClassPathXmlApplicationContext 包含着对 BeanFactory 所提供的一切特征, 那么这一步中将会复用 BeanFactory 中的配置文件读取解析其他功能, 这一步之后 ClassPathXmlApplicationContext 实际上就已经包含了 BeanFactory 所提供的功能, 也就是可以进行 Bean 的提取等基本操作了.
  3. 对 BeanFactory 进行各种功能填充
    @Qualifier@Autowired 应该是大家非常熟悉的注解了, 那么这两个注解正是在这一步骤中增加支持的.
  4. 子类覆盖方法做额外处理.
    Spring 之所以强大, 为世人所推崇, 除了它功能上为大家提供了便利外, 还有一方面是它完美的架构, 开放式的架构让使用它的程序员很容易根据业务需要扩展已经存在的功能. 这种开放式的设计在 Spring 中随处可见, 例如本例中就提供了一个空的函数实现 postProcessBeanFactory 来方便程序员在业务上做进一步的扩展.
  5. 激活各种 BeanFactory 处理器
  6. 注册拦截 bean 创建的 bean 处理器, 这里只是注册, 真正的调用是在 getBean 时候
  7. 为上下文初始化 Message 源, 及对不同语言进行国际化处理
  8. 初始化应用消息广播器, 并放入 applicationEventMulticaster bean 中
  9. 留给子类来初始化其他的 bean
  10. 在所有注册的 bean 中查找 listener bean, 注册到消息广播器中
  11. 初始化剩下的单实例 (非惰性的)
  12. 完成刷新过程, 通知生命周期处理器 lifecycleProcessor 刷新过程, 同时发出 ContextRefreshEvent 通知别人.

rainbow

这个人很懒,什么都没留下

文章评论