什么是 BeanDefinition?
BeanDefinition 直译为 bean 定义,描述了一个 bean 实例具有的构造方法参数和属性值等信息。与 Java 中的 Class 类似,Class 是类文件在内存中的表现形式,BeanDefinition 是 Spring Bean 配置元信息在内存中的表现形式,各种配置元信息最后都会被转换为 BeanDefinition ,Spring 根据 BeanDefinition 实例化、初始化 bean,BeanDefinition 涉及到 Spring Bean 的整个生命周期。
BeanDefinition 的分类及使用场景
在 Spring 3 之前,我们最常用 xml 配置 Spring 中的 bean,Spring 3 及之后,注解逐渐成为使用 Spring 的主要方式。此外较少使用的 bean 配置元数据还包括 properties、groovy,我们甚至可以定义自己的 BeanDefinition。Spring 中的 BeanDefinition 主要如下图所示。
eanDefinition 是所有 BeanDefinition 实现类的接口,其定义了基本的 BeanDefinition 信息,各实现可以包含其他不同的信息。BeanDefinition 包含的信息及含义如下所示。
AbstractBeanDefinition 是 BeanDefinition 的抽象实现,并在 BeanDefinition 接口的基础上添加了一些获取 BeanDefinition 其他信息的方法。
RootBeanDefinition 是合并后的 BeanDefinition,由于 BeanDefinition 可以具有父子关系,因此在运行时需要获取最终表示 bean 的 RootBeanDefinition ,其不能再获取父 BeanDefinition。
ChildBeanDefinition ,表示具有 父 BeanDefinition 的 BeanDefinition,使用较少。
GenericBeanDefinition,通用的 BeanDefinition,Spring 推荐编程式注册 BeanDefinition 时使用 GenericBeanDefinition,而不是使用 RootBeanDefinition。
AnnotatedBeanDefinition,注解 BeanDefinition,Spring 注解编程中使用的 BeanDefinition,包含类或方法的注解元信息。
如何创建 BeanDefinition
通常情况下,Spring 在解析不同的配置元信息时会使用不同的 BeanDefinition,我们不必进行关心。而如果我们想通过编程式向 Spring 注册 BeanDefinition ,我们则需要掌握创建 BeanDefinition 的一些技巧。具有两种创建 BeanDefinition 的方式。
直接通过 new 指令创建 BeanDefinition。
通过 BeanDefinitionBuilder 的静态方法创建 BeanDefinition ,这是建造者模式的一种实现,推荐使用这种方式创建 BeanDefinition。示例代码如下所示:
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(String.class) .setLazyInit(true).setAbstract(false).getBeanDefinition();
BeanDefinition 解析
BeanDefinition 通过 BeanDefinitionReader 解析配置元数据获取,BeanDefinitionReader 根据不同的配置元数据具有不同的实现,具体如下。
根据如上类图,可以发现,除了 BeanDefinitionReader 接口,还具有一个用于解析注解元数据的 AnnotatedBeanDefinitionReader ,这是因为 BeanDefinitionReader 解析的都是资源文件,而 AnnotatedBeanDefinitionReader 是 Spring 3 才添加的用来解析注解元数据为 BeanDefinition。
AbstractBeanDefinitionReader 是 BeanDefinitionReader 的抽象实现,主要将资源位置解析为资源 Resource。 PropertiesBeanDefinitionReader 用来解析 properties 文件,XMLBeanDefinitionReader 用来解析 xml 文件,GrovvyBeanDefinitionReader 用来解析 grovvy 文件。
目前最常用的为注解,在老旧的项目中可能存在 xml 配置文件,而 properties 和 groovy 则几乎没有用户使用。
注解元数据解析
AnnotatedBeanDefinitionReader 解析注解元数据为 BeanDefinition 的核心源码如下。
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier, @Nullable BeanDefinitionCustomizer[] customizers) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass); // 先进行 @Conditional 条件评估 if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; } abd.setInstanceSupplier(supplier); // 解析 scope 信息 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); // 处理通用的注解信息,如 @Primary、@Lazy 等 AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); if (qualifiers != null) { // 处理 qualifier 信息 for (Class<? extends Annotation> qualifier : qualifiers) { if (Primary.class == qualifier) { abd.setPrimary(true); } else if (Lazy.class == qualifier) { abd.setLazyInit(true); } else { abd.addQualifier(new AutowireCandidateQualifier(qualifier)); } } } // 自定义 BeanDefinition 信息 if (customizers != null) { for (BeanDefinitionCustomizer customizer : customizers) { customizer.customize(abd); } } // 创建 BeanDefinition BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); // 注册 BeanDefinition BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); }
可以看到,AnnotatedBeanDefinitionReader 实现就是读取类的配置信息,将其转换为对应的 BeanDefinition 属性,然后再创建 BeanDefinition 。需要注意的是,在扫描包时会使用 MetadataReader 读取类,底层使用 asm 读取类,因此不会进行类加载,会将类的元信息抽象为 AnnotationMetadata,此时直接创建 ScannedGenericBeanDefinition 对象。