【Spring探秘】Bean的前世今生之BeanDefinition简介

115 阅读4分钟

1. BeanDefinition是什么

在 Spring 中,BeanDefinition 是一个接口,用于描述一个 bean 对象,它是Spring创建 bean 的基石。其中保存了一个 bean 对象的元数据,比如:bean 的 Class 信息、作用域 (单例 or 原型)、依赖关系、初始化方法、销毁方法等信息。接口中相关方法如下(主要是一些get/set方法,没有复杂功能):

image.png

Spring 在启动过程就是通过 BeanDefinition 所提供的信息来创建 bean 对象的。 无论我们使用哪种 bean 的配置方式(注解 或 XML),Spring 在启动时都会解析这些配置,然后生成相应的 BeanDefinition 对象。这些 BeanDefinition 对象就像是 Spring 容器内部生成 bean 模板,告诉Spring容器如何创建和配置各个 Bean。

2. BeanDefinition的实现类

BeanDefinition 接口的实现类如下:

image.png

我们知道接口是对行为的一种抽象,通常用来定义有什么功能。而抽象类的主要目的是封装公共逻辑,复用代码。 上述 BeanDefinition 的实现类中,所有实现类都直接或间接的继承自 AbstractBeanDefinition,AbstractBeanDefinition 中定义了公共属性和公共方法。接下来介绍一下 BeanDefinition 的各个实现类。

2.1 ChildBeanDefinition

Spring 是支持 bean 继承的(这个平时用的并不多,了解即可),ChildBeanDefinition 正是用于继承父级 BeanDefinition 的 bean 定义。在解析 ChildBeanDefinition 的时候,会把父级 BeanDefinition 的属性拷贝给 ChildBeanDefinition。

具体来说,ChildBeanDefinition 内部持有一个标识父级 BeanDefinition 的属性parentName。需要注意的是 ChildBeanDefinition 没有对外暴露修改parentName的方法,所以 ChildBeanDefinition 一旦创建,父子关系就是不可改变的了。

public class ChildBeanDefinition extends AbstractBeanDefinition {
    // 父 BeanDefinition 名称
    @Nullable
    private String parentName;
}

注:Spring2.5 之后,推荐使用 GenericBeanDefinition 注册 BeanDefinition,我们可以通过 GenericBeanDefinition#setParentName 方法动态地设定父子依赖关系,GenericBeanDefinition 在大多数情况下取代了 ChildBeanDefinition。

2.2 GenericBeanDefinition

GenericBeanDefinition 是一个标准bean定义(我们自定义的大部分 bean 都会用这个类实现注册),同样可以设置父子关系(相比ChildBeanDefinition更加灵活)。如果在开发过程中,你需要通过编码的方式往 Spring 容器里注册BeanDefinition,Spring 更加推荐使用这个类来进行 bean 定义信息的注册。

一般来说,Spring 会使用 GenericBeanDefinition 类来注册我们自己所声明的bean(比如 XML 配置文件声明的bean)。

2.3. AnnotatedGenericBeanDefinition

AnnotatedGenericBeanDefinition 是对 GenericBeanDefinition 的扩展,增加了对于注解元数据的支持。我们平时写的配置类(即@Configuration注解标注的类)会被封装成AnnotatedGenericBeanDefinition,然后注册到 BeanDefinitionRegistry。

public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {
        // 注解元数据
        private final AnnotationMetadata metadata;
        @Nullable
        private MethodMetadata factoryMethodMetadata;
        
        // 省略其余代码.
}

2.4. ScannedGenericBeanDefinition

ScannedGenericBeanDefinition 功能上等同于2.3中的 AnnotatedGenericBeanDefinition,只是为了区分通过 扫描和其他方式得到的 BeanDefinition。

比如说,被组件扫描加载的 bean (@Component@Repository@Service@Controller)会以ScannedGenericBeanDefinition 的形式进行注册。

2.5 RootBeanDefinition

RootBeanDefinition 是所有bean定义信息在 Spring 运行时的统一视图。它是一个最完整的BeanDefinition,表示在运行时某个 bean 定义经过合并之后的 bean 定义(可能合并,也可能不合并,如果有父 BeanDefinition 就会合并)。

Spring 启动阶段,所有的 BeanDefinition 都会最终转换为RootBeanDefinition。

RootBeanDefinition 可以用于在配置阶段注册单个Bean定义。但是,自Spring 2.5 以后,以编程方式注册Bean定义的首选方式是 GenericBeanDefinition 类。GenericBean定义的优点在于它允许动态定义父依赖关系。

Spring 内置的 Bean 注册的都是通过创建 RootBeanDefiniton 的形式进行注册的,比如用于解析@Configuration、@Autowaire等注解的后置处理器的注册:

image.png

2.6 ConfigurationClassBeanDefinition

ConfigurationClassBeanDefinition 是 ConfigurationClassBeanDefinitionReader 的私有静态内部类。正如类名所表达的,它表示在配置类中的 Bean 定义,也即通过@Bean 注解定义的 bean 会被封装成 ConfigurationClassBeanDefinition。

2.7 ClassDerivedBeanDefinition

这是一个定义在 GenericApplicationContext 中的静态内部类,专门用于封装通过GenericApplicationContext#registerBean 方法注册的 Bean 定义信息。通过类图可知,它继承自 RootBeanDefinition,没有添加额外的功能,主要目的是作为一种标识,用于识别来自registerBean方法注册的 bean。

3. 总结

这篇文章主要介绍了 Spring 中各种 BeanDefinition 的实现,所有 BeanDefinition 在 Spring 运行时最终都会被转换成 RootBeanDefinition(后续文章会进行介绍)。真正开发过程中,我们并不需要关心 Spring 为某个 bean 创建的是哪一种 BeanDefinition。Spring会自动管理这些 BeanDefinition,并根据它们的类型以及它们所包含的信息来创建和配置bean。

虽然平时开发可能根本用不到 BeanDefinition,但却是搞懂 Spring 原理的基础,可以帮助我们更进一步的了解 Spring 的运行原理。