Spring 源码阅读(四)-BeanDefinition 基本概念
在之前的文章中,主要从Spirng IOC、Bean的实例化以及Bean之间的关系来学习了Spring的相关知识。这篇文章主要从BeanDefinition的相关知识
1 Spring BeanDefinition简介
If you work with an ApplicationContext
interface programmatically, child bean definitions are represented by the ChildBeanDefinition
class. Most users do not work with them on this level. Instead, they configure bean definitions declaratively in a class such as the ClassPathXmlApplicationContext
. When you use XML-based configuration metadata, you can indicate a child bean definition by using the parent
attribute, specifying the parent bean as the value of this attribute. The following example shows how to do so:
<bean id="inheritedTestBean" abstract="true" class="org.springframework.beans.TestBean"> <property name="name" value="parent"/> <property name="age" value="1"/> </bean> <bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBean" init-method="initialize"> <property name="name" value="override"/> <!-- the age property value of 1 will be inherited from parent --> </bean>
上面是来自Spring官网对于BeanDefinition的相描述。
总结来说:
BeanDefinition
包含了对bean做的配置,比如XML``标签的形式进行的配置- Spring将对bean的定义信息进行了抽象,抽象后的实体就是
BeanDefinition
,并且Spring会以此作为标准来对Bean进行创建。 BeanDefinition
包含以下元数据:- 一个全限定的类名,通常来说,就是对应的Bane的类名;
- Bean的行为配置元素,这些元素展示了这个Bean在容器中是如何工作的包括
scope
,lifecycle callbacks
(生命周期回调)等等; - Bean的依赖信息;
- 一些其它的相关配置信息。
比较正常创建一个Bean对象与通过Spring通过BeanDefinition创建一个Bean对象的不同:
正常创建一个Java Bean
通过
BeanBeanDefinition
来创建Bean
相对于正常的创建对象,Spring对其管理的bean没有直接采用new的方式,而是先通过解析配置数据以及根据对象本身的一些定义而获取其对应的BeanDefinition
,并将这个Beandefinition作为之后创建这个bean的依据。同时Spring在这个过程中提供了一些扩展点,例如在图中所提到了BeafactoryProcessor
。
2 BeanDefinition的方法分析
// 获取父BeanDefinition,主要 @Nullable String getParentName(); // 设置bean的className void setBeanClassName(@Nullable String beanClassName); // 设置bean的作用域 void setScope(@Nullable String scope); // 设置是否懒加载 void setLazyInit(boolean lazyInit); // 是否需要等待指定的bean创建完之后再创建 void setDependsOn(@Nullable String... dependsOn); // 是否作为自动注入的候选对象 void setAutowireCandidate(boolean autowireCandidate); // 是否作为主选的bean void setPrimary(boolean primary); // 创建这个bean的类的名称 void setFactoryBeanName(@Nullable String factoryBeanName); // 创建这个bean的方法的名称 void setFactoryMethodName(@Nullable String factoryMethodName); // 构造函数的参数 ConstructorArgumentValues getConstructorArgumentValues(); // setter方法的参数 MutablePropertyValues getPropertyValues(); // 生命周期回调方法,在bean完成属性注入后调用 void setInitMethodName(@Nullable String initMethodName); // 生命周期回调方法,在bean被销毁时调用 void setDestroyMethodName(@Nullable String destroyMethodName); // Spring可以对bd设置不同的角色,了解即可,不重要 // 用户定义 int ROLE_APPLICATION = 0; // 某些复杂的配置 int ROLE_SUPPORT = 1; // 完全内部使用 int ROLE_INFRASTRUCTURE = 2; void setRole(int role); /** * Set a human-readable description of this bean definition. * @since 5.1 */ void setDescription(@Nullable String description); // 根据scope判断是否是单例 boolean isSingleton(); // 根据scope判断是否是原型 boolean isPrototype(); // 跟合并beanDefinition相关,如果是abstract,说明会被作为一个父beanDefinition,不用提供class属性 boolean isAbstract(); }
BeanDefinition的继承关系
2.1 BeanDefinition实现的接口
org.springframework.core.AttributeAccessor
这个接口主要定义了元数据的访问接口。AbstractBeanDefinition
是实现AttributeAccessor
的抽象实现类之一。Spring通过这种方式,做到了数据接口与操作方法分离;接口中仅仅提哦那个了获取属性与设置属性的相关方法。而具体的数据实现则是在起实现类中去实现完成的;
org.springframework.beans.BeanMetadataElement
这个接口只有一个获取资源对象的的方法。
当定义了一个SpringBean
时,比如User
,这个时候可以理解为User
对应的BeanDefinition
通过GetSource
来获取 到User.class
所对应的File
对象;
如果通过@Bean
方式定义了一个User
的话,那么此时的source是被@Bean
注解所标注的一个Mehthod
对象。
2.2 AbstractBeanDefinition
org.springframework.core.AttributeAccessorSupport
AttributeAccessorSuppor
实现了AttributeAccerror
这个接口,AttributeAccerror
实现了访问者的设计模式,将数据结构跟操作方法进行了分离;而数据结构就在AttributeAccessorSupport
这个实现类中,其内部采用了LinkedHashMap
实现。
public abstract class AttributeAccessorSupport implements AttributeAccessor, Serializable { /** Map with String keys and Object values. */ private final Map<String, Object> attributes = new LinkedHashMap<>(); @Override public void setAttribute(String name, @Nullable Object value) { Assert.notNull(name, "Name must not be null"); if (value != null) { this.attributes.put(name, value); } else { removeAttribute(name); } } ......省略下面的代码
可以看到,在这个类中,维护了一个map,这就是BeanDefinition
体系中,通过访问者模式
所有操作的数据对象。
org.springframework.beans.BeanMetadataAttributeAccessor
这个类主要就是对上面的map中的数据操作做了更深一层的封装,就看其中的两个方法:
public void addMetadataAttribute(BeanMetadataAttribute attribute) { super.setAttribute(attribute.getName(), attribute); } public BeanMetadataAttribute getMetadataAttribute(String name) { return (BeanMetadataAttribute) super.getAttribute(name); }public void addMetadataAttribute(BeanMetadataAttribute attribute) { super.setAttribute(attribute.getName(), attribute); } public BeanMetadataAttribute getMetadataAttribute(String name) { return (BeanMetadataAttribute) super.getAttribute(name); }
可以发现,它只是将属性统一封装成了一个BeanMetadataAttribute
,然后就调用了父类的方法,将其放入到map中。
AbstractBeanDefinition
通过继承了BeanMetadataAttributeAccessor
这个类,可以对BeanDefinition
中的属性进行操作。这里说的属性仅仅指的是BeanDefinition
中的一个map,而不是它的其它字段。
对比BeanDefinition
的源码可以发现,AbstractBeanDefinition
对BeanDefinition
的大部分方法做了实现(没有实现parentName
相关方法)。同时定义了一系列的常量及默认字段。这是因为BeanDefinition
接口过于顶层,如果我们依赖BeanDefinition
这个接口直接去创建其实现类的话过于麻烦,所以通 AbstractBeanDefinition
做了一个下沉,并给很多属性赋了默认值。
// 默认情况不是懒加载的 private boolean lazyInit = false; // 默认情况不采用自动注入 private int autowireMode = AUTOWIRE_NO; // 默认情况作为自动注入的候选bean private boolean autowireCandidate = true; // 默认情况不作为优先使用的bean private boolean primary = false; ........
这样做的好处是我们在创建其它子类的时候,会方便很多。
2.3 AbstractBeanDefinition的三个子类
RootBeanDefinition
- Spring在启动时会实例化几个初始化的
BeanDefinition
,这几个BeanDefinition
的类型都为RootBeanDefinition
; - Spring在合并
BeanDefinition
返回的都是RootBeanDefinition
; - 我们通过
@Bean
注解配置的bean,解析出来的BeanDefinition
都是RootBeanDefinition
(实际上是其子类ConfigurationClassBeanDefinition
)
- Spring在启动时会实例化几个初始化的
ChildBeanDefinition
- 现在已经被
GenericBeanDefinition
所替代了。在5.1.x
版本没有找到使用这个类的代码。
- 现在已经被
GenericBeanDefinition
替代了原来的
ChildBeanDefinition
,比起ChildBeanDefinition
更为灵活,ChildBeanDefinition
在实例化的时候必须要指定一个parentName
,而GenericBeanDefinition
不需要。我们通过注解配置的bean以及我们的配置类(除@Bena
外)的BeanDefiniton
类型都是GenericBeanDefinition
。
2.4 AnnotatedBeanDefinition
AnnotatedBeanDefinition
实现了BeanDefinition
接口,其源码仅仅只有两个方法,如下:
/** * Obtain the annotation metadata (as well as basic class metadata) * for this bean definition‘s bean class. * @return the annotation metadata object (never {@code null}) */ AnnotationMetadata getMetadata(); /** * Obtain metadata for this bean definition‘s factory method, if any. * @return the factory method metadata, or {@code null} if none * @since 4.1.1 */ @Nullable MethodMetadata getFactoryMethodMetadata();
getMetadata(),主要用于获取注解元数据。主要用于保存通过注解方式定义bean所对应的
BeanDefinition
,提供一个获取获取注解信息的方法;
getFactoryMethodMetadata()
,这个方法跟的@Bean
注解相关。当在一个配置类中使用了@Bean
注解时,被@Bean
注解标记的方法,就被解析成了FactoryMethodMetadata
。
2.5 AnnotatedBeanDefinition的三个实现类
AnnotatedGenericBeanDefinition
通过形如下面的API注册的bean都是
AnnotatedGenericBeanDefinition
public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(); ac.register(Config.class); }
这里
Config
对象,最后在Spring容器中就是一个AnnotatedGenericBeanDefinition
;通过
@Import
注解导入的类,最后都是解析为AnnotatedGenericBeanDefinition
。ScannedGenericBeanDefinition
都过注解扫描的类,如
@Service
,@Compent
等方式配置的Bean都是ScannedGenericBeanDefinition
ConfigurationClassBeanDefinition
通过
@Bean
的方式配置的Bean为ConfigurationClassBeanDefinition
最后,还剩一个ClassDerivedBeanDefinition
,这个类是跟kotlin
相关的类,一般用不到,这里就不管了!
3 总结
至此,算是完成了BeanDefinition
部分的学习,在下一节中,将继续跟大家一起学习BeanDefinition
合并的相关知识。这篇文章中,主要学习了
什么是
BeanDefinition
,总结起来就是一句话,Spring创建bean时的建模对象。BeanDefinition
的具体使用的子类,以及Spring在哪些地方使用到了它们。这部分内容在后面的学习中很重要,画图总结如下: