Spring学习笔记:BeanDefinition

本文是自己学习的一个总结


1、什么是BeanDefinition

BeanDefinition是Spring Framework中定义Bean的配置原信息接口,其中包含

  • Bean的类名。它是一个全限定性名称。Bean是一个实例对象,Bean的类名就是指Bean的类型。
  • Bean的行为配置元素,如作用域,自动绑定的模式,生命周期回调等。
  • 其他Bean引用,又称为合作者(Collaborators)或者依赖(Dependencies)。合作者其实就是依赖关系,这里不用太在意。
  • 配置设置,比如Bean的属性。

1.1、BeanFactory中的BeanDefinition

BeanFactory获取对象的方式是这样的

//container是已经定义的一个容器,BeanFactory类型
ClassA a = container.getBean("a");




虽然BeanFactory会自动维护对象,但我们得先让容器知道哪些对象需要维护,这里就涉及到BeanFactory的对象注册和依赖绑定方式。

Spring对象注册和依赖绑定主要有直接编码方式、外部文件配置方式(这里不记录具体的绑定方式,只是记录依赖绑定的类BeanDefinitionRegistry和其他相关类)。

前面我们知道,BeanFactory内提供了获取容器中管理Bean对象的方法和一些获取Bean对象的属性的方法,但Bean被容器管理之前要先被注册,有依赖的建立依赖,而这些服务的提供者不是BeanFactory而是BeanDefinition。BeanDefinition和BeanFactory的关系图如下。
在这里插入图片描述

如果我们直接使用编码方式来绑定依赖的话,会使用同时继承BeanFactory和BeanDefinitionRegistry的DefaultListableBeanFactory来完成注册,然后将注册号的DefaultListableBeanFactory当做BeanFactory类型返回(这样的好处就是外界就只能调用作为BeanFactory类型的返回对象中,关于获取Bean的服务)。
下面的代码展示了这一过程,这段代码演示了使用DefaultListableBeanFactory注册几个Bean(注册Provider,Listener和Persister,其中Provider关联Listener和Persister,即Provider的构造函数的参数类型是Listener和Persiter),然后再从DefaultListableBeanFactory容器中取出其中一个Bean对象。

public static void main(String[] args) {
	DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
	BeanFactory container = (BeanFactory) bindViaCode(beanRegistry);
	Provider a = container.getBean("new provider");
}

BeanFactory bindViaCode(DefaultListableBeanFactory registry) {
	//将三个类放到BeanDefinition的实现类AbstractBeanDefinition中,等会再将他们注册到registry中
	AbstractBeanDefinition newProvider = new RootBeanDefinition(Provider.class, true);
	AbstractBeanDefinition newListener = new RootBeanDefinition(Listener.class, true);
	AbstractBeanDefinition newPersister = new RootBeanDefinition(Persister.class, true);
	//将三个类注册到容器中,还没完,因为Provider的构造函数需要另外两个类对象
	registry.registerBeanDefinition("new provider", newProvider);
	registry.registerBeanDefinition("new listener", newListener);
	registry.registerBeanDefinition("new persister", newPersister);
	//构造函数的注入依赖方式
	ConstructiorArgumentValues argValues = new ConstructorArgument.Values();
	argValues.addIndexedArgumentValue(0, newListener);
	argValues.addIndexedArgumentValue(0, newListener);
	newProvider.setConstructorArgumentValues(argValues);
	//已BeanFactory的类型返回,这样客户端就只能调用获取Bean的方法,不能再新注册Bean到容器中,有利于封装。
	return (BeanFactory) registry;
}








当然,现在写工程项目很少会用到代码手动注册的方式,注解@Autowired会用的比较多,这里只是为了展示DefaultListableBeanFactory、BeanDefinition、BeanFactory的关系才贴上这些代码。


2、BeanDefinition中的元信息

属性 说明
beanClass Bean的全类名,必须是具体类,不能是抽象类或接口。因为Bean是实例对象,抽象类或接口是不能实例的,不能作为Bean的类名。
name Bean的名称或者id
scope Bean的作用域,比如默认的单例模式,还有原生模式。
constructorArgumentValues Bean的构造器参数,用于依赖注入。
propertiesValues Bean中成员属性
autowireMode 自动绑定的模式,比如byName,通过名称自动绑定;byType,通过类型自动绑定等。这是可以设置的。
lazyInit boolean值,是否延迟初始化。默认是非延迟初始化。延迟化的好处是可以有效地减少启动时间。
initMethodName Bean初始化回调方法名称
destroyMethodName Bean销毁回调方法名称

3、BeanDefinition的构建

BeanDefinition的构建主要有两种方法。

  • 通过BeanDefinitionBuilder。
  • 通过AbstractBeanDefinition以及派生类。

3.1、BeanDefinitionBuilder构建BeanDefinition

3.1.1 childBeanDefinition、rootBeanDefinition和genericBeanDefinition的区别

BeanDefinitionBuilder内部有许多静态方法构建BeanDefinition。这些静态方法分为以下三种,所有的静态方法这三个方法的重载形式。

  • childBeanDefinition()
  • rootBeanDefinition()
  • genericBeanDefinition()

三者从名字上其实就可以看出区别。虽然三个方法返回的都是BeanDefinitionBuilder,但是三者其实是和三个BeanDefinition相关。rootBeanDefinition相关的是RootBeanDefinition。RootBeanDefinition和ChildBeanDefinition是相互配合的,ChildBeanDefinition可以直接继承RootBeanDefinition中的元信息。比如下面的代码。

//假设容器中已定义一个RootBeanDefinition命名为rootBeanDefinition。
//下面这段语句就令childBeanDefinition直接快速继承rootBeanDefinition中的元信息。
//GenericBeanDefinition就没办法这样快速继承。
ChildBeanDefinition childBeanDefinition = new ChildBeanDefinition("rootBeanDefinition");

GenericBeanDefinition就相关一个普通的GenericBeanDefinition。三者其实都是一样的,只是GenericBeanDefinition没法像ChildBeanDefinition和RootBeanDefinition一样相互联动。

3.1.2 BeanDefinitionBuilder构建BeanDefinition的方式

下面我们使用简单示例,使用genericBeanDefintion构建简单的BeanDefinition。
我们想构建一个描述User类的BeanDefinition。User类的描述如下。

public class User {
    private Long id;
    private String name;
  	//省略setter和getter
}

下面开始通过BeanDefinitionBuilder构建BeanDefinition

BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
//设置Bean的属性
beanDefinitionBuilder.addPropertyValue("name", "用户1");
beanDefinitionBuilder.addPropertyValue("id", 10);
//获取到BeanDefintion
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();



打开断点可以看到beanDefinition的信息如下。
在这里插入图片描述

3.1.3 通过AbstractBeanDefinition及其派生类构建BeanDefinition

这种方式相较于上一种就比较直接,我们不借用BeanDefinitonBuilder这个中间工具来构建BeanDefinition,而是直接创建一个BeanDefinition,然后直接设置元信息。
AbstractBeanDefintion的子类中最常见和通用的就是GenericBeanDefintion,下面举例也是使用GenericBeanDefintion。

//直接new一个GenericBeanDefinition 
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
//然后设置要描述的类
genericBeanDefinition.setBeanClass(User.class);
//设置类的Property,不能像BeanDefinitionBuilder一样直接设置,要通过MutablePropertyValues这个中间人
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.addPropertyValue("name", "用户1");
propertyValues.addPropertyValue("id", 10); //也可以写成propertyValues.add("id", 1).add("name", "用户1");
genericBeanDefinition.setPropertyValues(propertyValues);


4、将BeanDefinition注册到容器中

回到这张图,将BeanDefinition定义好之后,还要将其注册到容器中
在这里插入图片描述
经常使用的注解和xml方式装配bean实际上就是将bean信息转换成BeanDefinition,然后将BeanDefiniion注册到容器中。本质上就是调用下面三种API。

  • 命名方式:BeanDefinitionRegistry#registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  • 非命名方式:BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition definition, BeanDefinitionRegistry registry)
  • 配置类方式:AnnotatedBeanDefinitionReader#register(Class<?>… componentClasses)

4.1、命名方式注册

要注意到,GenericApplicationContext和DefaultListableBeanFactory这样基本容器类是直接实现BeanDefinitionRegistry这个接口的,所以大部分容器类都可以直接看成是BeanDefinitionRegistry运行registerBeanDefinition方法。
在这里插入图片描述

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
//设置Bean的属性
beanDefinitionBuilder.addPropertyValue("name", "用户1");
beanDefinitionBuilder.addPropertyValue("id", 10);
//获取到BeanDefintion并注册
applicationContext.registerBeanDefinition("user", beanDefinitionBuilder.getBeanDefinition());




之后就可以在applicationContext中取出user这个bean了。

4.2、非命名方式注册

这个和命名方式注册类似,只是不指定注册bean的名称。bean的默认名称为『全限定性类名』+『#编号』。名称的生成规则和基本的装配bean且不指定名称方式规则一样。

BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, applicationContext);


4.3、配置类方式注册

这个方式其实也是直接通过applicationContext.register来调用,效果和new AnnotationConfigApplicationContext(Class)的效果一样。

猜你喜欢

转载自blog.csdn.net/sinat_38393872/article/details/106820610