BeanDefinition
封面来源:碧蓝航线 特殊背景
0. 前言
7 月中旬介绍了 SpringBoot 中的事件监听机制,一句话概括就是 Spring 环境下观察者模式的实现。在这篇文章中,我曾信誓旦旦地说到要开启“爆肝”模式,原本也是这么计划的,奈何身体抱恙,又去了一趟医院,结果也没检查出个啥,在医生开了几盒药之后留下一句“再观察下”便草草了事。
也不知道身体究竟有没有毛病,感觉在跑了几次医院后,就算没病也焦虑出病了。暂且先不管那么多,保持好心情,拒绝焦虑,拒绝熬夜,做好这几点总归是没有坏处的,后续再找个时间去华西转一圈,如果还是没什么结果,那可能真的是心理作用吧。
如果有朋友恰好看到了我这段文字,希望陌生的你也能保持一个好心情,拒绝焦虑,拒绝熬夜。说到熬夜,网上无论是文字,还是视频,总有无数种方式教年轻人怎么不熬夜,要我说,这些都没用,年轻人真要熬夜,那是拦不住的,只有他自己意识到不该熬夜了,他才不会再熬夜。要说这个意识从何而来,等他叫一次 120 自然就知道了,至少我是这样的。
总之,生命短暂,拒绝熬夜。
言归正传,原计划是在 Spring 的事件监听机制 一文后介绍 SpringBoot 的自动配置机制,但在构思时发现在这之前还需要花点篇幅介绍 @Conditional
注解的使用与实现,然后又发现漏掉了 Spring 中一个很重要的概念 —— BeanDefinition
,它是 Spring 管理 Bean 的过程中绕不开的一个类。
为了后续阅读源码时更加得心应手,本文既不介绍 SpringBoot 的自动配置机制,也不介绍 @Conditional
注解的实现,而是剖析 BeanDefinition
体系。
1. BeanDefinition
BeanDefinition
继承了 AttributeAccessor
和 BeanMetadataElement
:
AttributeAccessor
:提供了对 属性 的操作,表明BeanDefinition
能够操作属性BeanMetadataElement
:用于获取 Bean 的配置源对象,比如 XML 中的某个元素、注解等。在 Spring 中,常用于调试、诊断和报告错误,不必过于关心这个接口。
一个 BeanDefinition
能够描述一个 Bean 实例,包括属性值、构造器参数值、以及具体实现提供的其他信息。它是一个最小化的接口:其主要目的是允许 BeanFactoryPostProcessor
内省和修改属性值及其他 bean 元数据。
内省,introspection,运行时检查对象(包括类、接口、字段和方法)的一种能力,而不需要了解对象的具体实现。
1 | public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { |
2. AbstractBeanDefinition
AbstractBeanDefinition
是 BeanDefinition
的一个抽象子类,BeanDefinition
的其他实现类都会继承 AbstractBeanDefinition
。
1 | public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor |
2.1 内部常量
AbstractBeanDefinition
内部定义了许多常量,这些常量大多与自动装配(注意自动装配和自动配置的区别)有关。
作用域
1 | public static final String SCOPE_DEFAULT = ""; |
默认 scope
值,等价于 singleton
。
自动装配
1 | public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO; |
设置自动装配模式时使用,即调用 setAutowireMode()
方法时使用。当前模式表示不需要自动装配,需要手动注入。
1 | public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME; |
同上。当前模式表示按照 Bean 的 name
进行自动装配。
1 | public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE; |
同上。当前模式表示按照 Bean 的类型进行自动装配。
1 | public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR; |
同上。当前模式表示按照构造器参数类型进行自动装配,和 byType
很类似。
1 |
|
同上。这是一种在 Spring 3.0 中被标记为废弃的模式,在这种模式下将先按照构造器进行自动装配,如果失败,再尝试通过 byType
的方式进行自动装配。
从注释中得知,如果想要使用混合的自动装配策略,应该使用基于注解的自动装配,以便更清楚地明确自动装配的需求。
依赖检查
1 | public static final int DEPENDENCY_CHECK_NONE = 0; |
没有依赖项,无需检查。
1 | public static final int DEPENDENCY_CHECK_OBJECTS = 1; |
需要进行对象间引用的检查。
1 | public static final int DEPENDENCY_CHECK_SIMPLE = 2; |
需要进行“简单”属性的依赖检查。
可以通过 BeanUtils#isSimpleValueType()
方法来判断哪种类型是“简单”类型,在 Spring 6.1 中,该方法被委派给 ClassUtils.isSimpleValueType()
实现,后续可能会将更多类型纳入“简单”。
暂时根据 BeanUtils#isSimpleValueType()
方法的注释可知,以下类型被划为“简单”:
- 基本类型及其包装类型
- 枚举
- 字符串,即
String
、CharSequence
- 数字
Number
- 时间,即
Date
、Temporal
- 其他类型,包括
UUID
、URI
、URL
、Locale
和Class
注意: Void
和 void
并不“简单”。
1 | public static final int DEPENDENCY_CHECK_ALL = 3; |
需要对所有属性进行依赖项检查,相当于是 DEPENDENCY_CHECK_OBJECTS
和 DEPENDENCY_CHECK_SIMPLE
的融合模式。
其他常量
1 | public static final String PREFERRED_CONSTRUCTORS_ATTRIBUTE = "preferredConstructors"; |
Spring 6.1 中新增的常量,它是一个 BeanDefinition
的属性名(attribute name),以便 BeanDefinition
能够指定一个或多个首选构造器。这类似于 Bean 对应的类中被 @Autowired
标记的构造器。
该属性名对应的属性值可以是一个 Constructor
对象,也可以是其数组。
1 | public static final String ORDER_ATTRIBUTE = "order"; |
Spring 6.1 中新增的常量,它是一个 BeanDefinition
的属性名(attribute name),以便 BeanDefinition
能够指定目标 Bean 的排序顺序。这类似于 @Order
注解。
1 | public static final String INFER_METHOD = "(inferred)"; |
Bean 的销毁方法的推断值。以 @Bean
注解为例,其 destroyMethod
属性的默认值就是该值,如果未显式设置该值,在 Bean 的销毁期间,Spring 会尝试推断对应类中的销毁方法。
目前,Spring 将依次判断类中是否存在名为 close
和 shutdown
的方法,如果存在,Spring 将认定它们为未显式指定的销毁方法,并在销毁期间执行那个方法。
2.2 成员变量
相比于常量,AbstractBeanDefinition
内部定义了更多的成员变量,这些成员变量也体现了 Bean 的特性。
1 |
|
Bean 对应的 Class 对象。
1 |
|
Bean 的作用域。
1 | private boolean abstractFlag = false; |
是否是抽象。
1 |
|
是否懒加载,或者说延迟加载。
1 | private int autowireMode = AUTOWIRE_NO; |
自动装配模式,默认不自动装配。
1 | private int dependencyCheck = DEPENDENCY_CHECK_NONE; |
依赖检查,默认没有依赖。
1 |
|
依赖列表。
1 | private boolean autowireCandidate = true; |
是否是自动装配时的候选值,默认是。
1 | private boolean primary = false; |
是否是首选。
1 | private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>(); |
存放的 Qualifier 信息,以便自动装配时确定被装配的 Bean。与 @Qualifier
和 @Autowired
注解有关。
1 |
|
获取 Bean 实例。
1 | private boolean nonPublicAccessAllowed = true; |
是否允许访问非 public
的构造器。
1 | private boolean lenientConstructorResolution = true; |
是否以宽松模式来解析构造器。如果存在多个候选的构造器,在严格模式下,会抛出异常;而在宽松模式下,Spring 会选择最合适的构造器进行注入,以免抛出异常,这提升了 Spring 的灵活性和容错能力。
1 |
|
构造当前 BeanDefinition
的工厂 Bean 的名称。
1 |
|
构造当前 BeanDefinition
的工厂方法名称。
1 |
|
构造器的参数值,这些值也是注入的属性值。
1 |
|
注入的属性值。
1 | private MethodOverrides methodOverrides = new MethodOverrides(); |
管理和存储与方法重载的相关信息,在处理 Bean 中的方法替换和方法注入时有用。
方法替换,Method Replacer,使用 ReplaceOverride
类表示方法替换。允许在运行时替换 Bean 中的方法实现,而不需要修改原始类的代码。
方法注入,Lookup Method Injection,使用 LookupOverride
类表示方法注入。允许在运行时注入一个新的 Bean 实例到方法中,而不需要修改原始类的代码。
1 |
|
初始化方法。
1 |
|
销毁方法。
1 | private boolean enforceInitMethod = true; |
是否强制执行初始化方法。
1 | private boolean enforceDestroyMethod = true; |
是否强制执行销毁方法。
1 | private boolean synthetic = false; |
当前 BeanDefinition
是否是“合成的”(synthetic),所谓“合成的”,就是由框架内部生成的,而不是用户自定义的。比如 AOP 中生成的代理 BeanDefinition
就是由 Spring 自动生成的,会被标记为“合成的”。
1 | private int role = BeanDefinition.ROLE_APPLICATION; |
Bean 的角色,默认为应用程序级别。
1 |
|
Bean 的描述信息。
1 |
|
当前 BeanDefinition
来自的资源(Resource)。
2.3 构造器与方法
AbstractBeanDefinition
中的构造器都比较简单,没有什么特殊逻辑,就不再赘述。
AbstractBeanDefinition
中的方法大多数操作成员变量的 Getter、Setter,没啥营养,也略过。
2.4 RootBeanDefinition
RootBeanDefinition
是 AbstractBeanDefinition
的一个实现,它表示 运行时合并的 BeanDefinition
。RootBeanDefinition
可以是由多个相互继承的原始 BeanDefinition
创建的。
在 Spring 中,子 BeanDefinition
可以从父 BeanDefinition
中继承属性。RootBeanDefinition
可以用于那些没有父 BeanDefinition
的 根 BeanDefinition
,也可以用于合并父 BeanDefinition
的属性。
RootBeanDefinition
在 AbstractBeanDefinition
的基础上定义了更多的成员变量。
1 |
|
存储了 Bean 的名称、别名、BeanDefinition
。三个愿望,一次满足!🥪
1 |
|
AnnotatedElement
由 JDK 提供,通过该成员变量能够查看 Bean 的注解信息。
1 | volatile boolean stale; |
确定是否需要重新合并当前 BeanDefinition
。
1 | boolean allowCaching = true; |
是否允许缓存。
1 | boolean isFactoryMethodUnique; |
工厂方法是否唯一。
1 |
|
当前 BeanDefinition
对应的 ResolvableType
。
ResolvableType
的使用可以参考 Spring 的事件监听机制 的第一节。
1 |
|
缓存给定 BeanDefinition
对应的 Class
信息。
1 |
|
缓存当前 Bean 是否是工厂 Bean。
1 |
|
缓存工厂方法的返回值类型。
1 |
|
缓存唯一的工厂方法候选值。
1 |
|
缓存销毁方法的名称(包括推断出的销毁方法名称)。
1 | // 以下四个构造器字段的通用锁 |
1 | // 以下两个后处理字段的公共锁 |
1 |
|
保存由外部管理的配置成员(成员变量、成员方法)。
1 |
|
保存由外部管理的初始化方法,比如被 @PostConstruct
注解标记的方法。
1 |
|
保存由外部管理的销毁方法,比如被 @PreDestroy
注解标记的方法。
2.5 ChildBeanDefinition
ChildBeanDefinition
也是 AbstractBeanDefinition
的一种实现,它表示继承了父 BeanDefinition
相关设置的一种 BeanDefinition
。子 BeanDefinition
对父 BeanDefinition
具有固定的依赖关系。
子 BeanDefinition
可以从父 BeanDefinition
继承构造器参数值、属性值和 methodOverrides
信息,同时也可以添加新的值。子 BeanDefinition
可以覆盖父 BeanDefinition
的初始化方法、销毁方法和静态工厂方法。其余剩下的设置将始终从子 BeanDefinition
中获取,比如依赖关系、自动装配模式、依赖检查、是否单例、是否懒加载。
从 Spring 2.5 开始,通过编程方式注册 BeanDefinition
时,更推荐的方式是使用 GenericBeanDefinition
,通过调用其 setParentName()
方法能够动态地定义父子依赖关系。这实际上取代了大多数使用场景中的 ChildBeanDefinition
类。
ChildBeanDefinition
的实现比较简单,仅有一个 String
类型的成员变量 parentName
:
1 | public class ChildBeanDefinition extends AbstractBeanDefinition { |
通过构造 ChildBeanDefinition
时传入 parentName
,或者调用 setParentName()
方法设置 parentName
,能够指定对应父 BeanDefinition
的名称。
2.6 GenericBeanDefinition
GenericBeanDefinition
是 Spring 2.5 引入了的 BeanDefinition
,它能够很好地替代 ChildBeanDefinition
。
1 | public class GenericBeanDefinition extends AbstractBeanDefinition { |
GenericBeanDefinition
和 ChildBeanDefinition
的实现很类似,也可以通过调用 setParentName()
方法设置父 BeanDefinition
的名称。
尽管两者很类似,但它们表示不同的含义。
除此之外,ChildBeanDefinition
是不能脱离父 BeanDefinition
单独存在的,而 GenericBeanDefinition
可以 按需 设置父 BeanDefinition
的名称。
2.7 区别
GenericBeanDefinition
:
- 一个 通用的
BeanDefinition
,适用于大多数场景。
ChildBeanDefinition
:
- 一个 继承于 其他
BeanDefinition
的BeanDefinition
,不能脱离父BeanDefinition
; - 常在 XML 配置文件中使用;
- 自 Spring 2.5 之后,它逐渐被
GenericBeanDefinition
取代。
RootBeanDefinition
:
- 一个 顶级
BeanDefinition
,没有父BeanDefinition
的BeanDefinition
: - 包含完整
BeanDefinition
的信息,不依赖于其他 Bean。
3. AnnotatedBeanDefinition
AnnotatedBeanDefinition
是 BeanDefinition
的子接口,能够用来获取注解元数据。一般情况下,通过注解方式得到的 Bean(比如 @Component
、@Bean
),其 BeanDefinition 类型都是该接口的实现类。
1 | public interface AnnotatedBeanDefinition extends BeanDefinition { |
通过 AnnotationMetadata
能够获取到对应 类 及其 注解 的元数据信息,这是因为 AnnotationMetadata
又继承了 ClassMetadata
和 AnnotatedTypeMetadata
:
1 | public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata { |
MethodMetadata
和 AnnotationMetadata
类似,能够获取到对应 方法 及其 注解 的元数据信息:
1 | public interface MethodMetadata extends AnnotatedTypeMetadata { |
AnnotationMetadata
与 MethodMetadata
提供的 API 都比较简单、清晰,这里不再讲解它们的用法。
4. 其他实现
4.1 AnnotatedGenericBeanDefinition
1 | public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition { |
AnnotatedGenericBeanDefinition
继承 GenericBeanDefinition
还实现了 AnnotatedBeanDefinition
,该类用来描述通过注解获取到的 BeanDefinition
。
AnnotatedGenericBeanDefinition
重写了 getFactoryMethodMetadata()
方法:
1 |
|
表示该 BeanDefinition
是可以由工厂方法创建。
使用了 @Configuration
注解标记的配置类会解析为 AnnotatedGenericBeanDefinition
。
4.2 ScannedGenericBeanDefinition
ScannedGenericBeanDefinition
和 AnnotatedGenericBeanDefinition
类似,但它重写 getFactoryMethodMetadata()
方法直接返回了 null
:
1 | public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition { |
通过包扫描自动注册的 Bean 会被解析为 ScannedGenericBeanDefinition
,比如被 @Component
及其派生注解标记的类。
4.3 ConfigurationClassBeanDefinition
ConfigurationClassBeanDefinition
是位于 ConfigurationClassBeanDefinitionReader
类中的一个静态嵌套类:
1 | private static class ConfigurationClassBeanDefinition extends RootBeanDefinition implements AnnotatedBeanDefinition { |
在被 @Configuration
注解标记的配置类中,通过 @Bean
注解定义的 Bean 会被解析成 ConfigurationClassBeanDefinition
。
继承 RootBeanDefinition
也表示 @Bean
注解定义的 Bean 是一个顶级 Bean,已经包含了全部 BeanDefinition
信息。