一、导读

BeanDefinition 是 Spring 框架中定义 Bean 的配置元信息接口,包含有:

  • Bean的类名
  • Bean行为配置元素,如 作用域 、 自动绑定模式 、 生命周期回调 (注解、方法)等
  • 其他Bean引用,可称为 合作者(Collaborators) 或 依赖(Dependencies)
  • 配置设置,比如 Bean 属性(Properties) | 属性 | 说明 | | —- | —- | | Class | Bean全类名,必须是具体类,不能用抽象类或接口 | | Name | Bean的名称或ID | | Scope | Bean的作用域(singleton、prototype) | | Constructor arguments | Bean构造器参数(用于依赖注入) | | Properties | Bean属性设置(用于依赖注入) | | Autowiring Mode | Bean自动绑定模式(NO(default)、byType、byName) | | Lazy Initialization Mode | Bean延迟初始化模式(延迟、非延迟) | | Initialization Mothod | Bean初始化回调方法名称 | | Destruction Method | Bean销毁回调方法名称 |

对于 厨师 而言, BeanDefinition 就是一份菜谱,里面记录着一道菜从食材的选择到烹饪的火候等一系列操作,也像 曲谱 ,由乐者奏出美妙 音符 ♪。

二、从上帝视角看Spring的BeanDefinition体系结构

2.1 继承结构

BeanDefinition.png

2.2 剖析

1. AttributeAccessor

见名知义,属性获取接口。该接口定义了一个 通用的准则 获取任意实体的元数据。简单看一下该接口的方法:

  1. // AttributeAccessor
  2. public interface AttributeAccessor {
  3. void setAttribute(String name, @Nullable Object value);
  4. Object getAttribute(String name);
  5. Object removeAttribute(String name);
  6. boolean hasAttribute(String name);
  7. String[] attributeNames();
  8. }

2. BeanMetadataElement

// BeanMetadataElement
// 
public interface BeanMetadataElement {

    /**
     * Return the configuration source {@code Object} for this metadata element
     * (may be {@code null}).
     */
    @Nullable
    default Object getSource() {
        return null;
    }

}

由Bean元数据元素实现,该对象携带一个资源对象。如下图所示:
image.png
可以理解 .class 源文件。当然,如果被在配置类中被@Bean 注解标注的话,那么此时的 getSource 方法返回的是 StandardMethodMetadata 对象。表示该 Bean 是来源于某个配置方法。如下图所示:
beandefinition-source.png

3.BeanDefinition

BeanDefinition 描述了一个bean实例,该实例具有 属性值 、 构造函数参数值 以及由具体实现类提供的更多信息。 这只是一个最小的接口:主要目的是允许 BeanFactoryPostProcessor 内省和 修改属性值 和其他Bean元数据信息。

说到 内省 ,我们看一下里面的 setter 方法:
beandefinition-setter-method.png
还有 isXX 方法:
beandefinition-is-method.png
是不是很熟悉,大致与我们常用的注解,如 @Lazy 、 @Primary 等对应。解释几个少见的:

  • 生命周期回调方法
    • setInitMethodName
    • setDestroyMethodName
  • 父类信息
    • getParentName()
  • 获取构造函数参数
    • getConstructorArgumentValues()
  • 获取setter 方法参数
    • getPropertyValues()
  • 创建此类的类名
    • setFactoryBeanName(@Nullable String factoryBeanName)
  • 创建此类的方法名称
    • setFactoryMethodName(@Nullable String factoryMethodName);

当前 BeanDefinition 定义最基础的Bean信息,更丰富的信息,比如注解相关的,由子类完成。

4.AbstractBeanDefinition

抽象类,继承 BeanDefinition 接口,实现 BeanDefinition 绝大部分接口,但排除 GenericBeanDefinition 、 RootBeanDefinition 和 ChildBeanDefinition 常用属性。里面定义了接口所需要的 数据结构 以及赋予默认值。方便子类扩展。

5.GenericBeanDefinition

Generic: 通用的。
它可以灵活设置 parentName 属性值,从 父bean 中继承属性值,甚至可以通过 后置处理器 重新配置父名称。在父/子关系恰好是预先确定的地方使用 RootBeanDefinition / ChildBeanDefinition 。

6.ScannedGenericBeanDefinition

/**
 * 基于ASM ClassReader的GenericBeanDefinition类的扩展,支持通过AnnotatedBeanDefinition接口获取注解元信息。
 *
 * 此类不会提早加载Bean类。
 * 而是从ASM ClassReader解析的“.class”文件本身以检索所有相关的元数据。
 * 它在功能上等效于AnnotatedGenericBeanDefinition.AnnotatedGenericBeanDefinition(AnnotationMetadata),
 * 但按类型区分已扫描的bean 和 已通过其他方式注册或检测到的bean。
 *
 */
@SuppressWarnings("serial")
public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {

    private final AnnotationMetadata metadata;


    /**
     * 为给定的MetadataReader描述的类创建一个新的ScannedGenericBeanDefinition
     * @param metadataReader the MetadataReader for the scanned target class
     */
    public ScannedGenericBeanDefinition(MetadataReader metadataReader) {
        Assert.notNull(metadataReader, "MetadataReader must not be null");
        this.metadata = metadataReader.getAnnotationMetadata();
        setBeanClassName(this.metadata.getClassName());
        setResource(metadataReader.getResource());
    }


    @Override
    public final AnnotationMetadata getMetadata() {
        return this.metadata;
    }

    @Override
    @Nullable
    public MethodMetadata getFactoryMethodMetadata() {
        return null;
    }
}

通过类扫描的都会变为此 ScannedGenericBeanDefinition 。
beandefinition-map.png

7.AnnotationGenericBeanDefinition

GenericBeanDefinition类的扩展,可以通过AnnotatedBeanDefinition接口暴露元注解信息。

//AnnotationGenericBeanDefinition
public AnnotatedGenericBeanDefinition(Class<?> beanClass) {
    setBeanClass(beanClass);
    // 内省当前BeanClass进行注解元数据
    this.metadata = AnnotationMetadata.introspect(beanClass);
}

在 Spring 内部中,使用 AnnotationConfigApplicationContext#register 或该类 构造方法 注册的 BeanDefinition 就是此类型。 注解@Import 导入的类也是此类型。

与ScannedGenericBeanDefinition区别

ScannedGenericBeanDefinition 是在将类路径扫描所得的类信息封装为此类型,采用的是 ASM 字节码技术,而 AnnotatedGenericBeanDefinition 使用标准反射API完成注解信息获取。

8.RootBeanDefinition

  • 根bean定义表示已合并的bean definition,该definition在运行时支持Spring BeanFactory中的特定bean。
  • 它可能是由多个相互继承的原始bean定义创建的,通常为GenericBeanDefinitions。
  • root bean definition本质上是运行时的“统一”bean定义视图。
  • 根Bean定义也可以在配置阶段用于注册各个Bean定义。
  • 但是,从Spring 2.5开始,以编程方式注册bean定义的首选方法是GenericBeanDefinition类。
  • GenericBeanDefinition的优点在于,它允许动态定义父项依赖项,而不是将角色“硬编码”为根bean定义

回顾Spring创建 RootBeanDefinition ,有以下几点:

  • 在 AnnotationConfigApplicationContext 构造器中通过 AnnotationConfigUtils 注册6个重要的Spring内建对象,都是使用RootBeanDefinition 封装。
  • Spring在 DefaultListableBeanFactory#getMergedLocalBeanDefinition返回的对象为RootBeanDefinition。
  • @Bean解析生成的也是RootBeanDefinition(实际上是其子类ConfigurationClassBeanDefinition)

    9.ChildBeanDefinition

    从 Spring 2.5 开始,以编程的方式注册 BeanDefinition ,首选是 ChildBeanDefinition ,它可以动态定义父类依赖项。对大多数用例而言,使用 GenericBeanDefinition 取代 ChildBeanDefinition 。

    2.3 小结

    相信各位看到这里,对 Spring 的 BeanDefiniton 有个大概的了解。不同层次、不同类型的 BeanDefinition 实现类表示拥有不同的能力。具有结构性的接口继承,便于维护和管理。在 Spring 内部使用 RootBeanDefinition 来做最后的菜谱,从里面获取配置信息实例化Bean对象。

    三、BeanDefinition合并操作

    若类存在继承关系,需要获取 merge beandefiniton 完成对 BeanDefinition 意义上的 继承 。显而意见

  • 父 BeanDefinition 存在,子不存在,则子类继承其没有的属性值。

  • 若父、子存在相同属性,子不会被父类属性覆盖。

即没有的的拿过来,有的就不用管。