前言
BeanDefinition
是Spring中的十分重要的一个类,学习Springboot和Spring时,会经常遇到该类,所以本篇文章会对BeanDefinition
的概念进行入门学习,同时一并学习定义注册,移除和查询BeanDefinition
的BeanDefinitionRegistry
接口。本篇文章用于帮助理解BeanDefinition
的概念,同时作为笔者学习Springboot的笔记,有概念错误的地方敬请批评指正。
Springboot版本:2.4.1
正文
一. BeanDefinition简析
BeanDefinition
是Spring中的重要接口,BeanDefinition
的实现类用于描述Spring中的一个应该被实例化的bean的各种性质,包括bean的属性值,构造函数,方法等信息,除此之外,还额外描述bean在Spring容器中的作用域,bean名称等信息。
可以将BeanDefinition
类比于Java
中的类的Class
对象,在Java
中可以使用一个类的Class
对象来完成对象的实例化,但是在Spring中,单纯使用bean的Class
对象无法完成bean的实例化,因为Spring中的bean具备一些额外的性质,例如bean是否是单例,bean是否在容器中是懒加载,bean在容器中的名字,这些性质无法依靠类的Class
对象来描述,所以Spring引入BeanDefinition
来描述Spring中的一个应该被实例化的bean的各种性质。
Spring框架在启动时,会在ConfigurationClassPostProcessor
这个bean工厂后置处理器中将需要被加载到容器中的bean扫描到并创建BeanDefinition
,然后缓存到BeanFactory
的beanDefinitionMap中,beanDefinitionMap是一个Map
,用于存放BeanDefinition
,键为bean在容器中的名称,值为bean对应的BeanDefinition
。
现在以Springboot启动为例,简要展示ConfigurationClassPostProcessor
将需要被加载到容器中的bean扫描到并创建BeanDefinition
然后放到beanDefinitionMap中的一个流程。在Springboot启动时,会先创建容器(也叫应用上下文),然后调用SpringApplication
的refreshContext()
方法来初始化容器,在refreshContext()
方法中会最终调用到容器的refresh()
方法来完成初始化,这个调用链可以表示如下。
在AbstractApplicationContext
的refresh()
方法中,会调用invokeBeanFactoryPostProcessors()
方法来调用bean工厂后置处理器,ConfigurationClassPostProcessor
就会在这里被调用,具体的ConfigurationClassPostProcessor
的逻辑这里暂时不分析,现在将断点打到AbstractApplicationContext#refresh()
方法中调用invokeBeanFactoryPostProcessors()
方法的这一行代码,此时观察BeanFactory
中的beanDefinitionMap如下所示。
事先已经定义好了一个bean,如下所示。
@Component
public class TestBean {
public TestBean() {
System.out.println("Initialize TestBean.");
}
}
此时往后执行一步,再观察BeanFactory
中的beanDefinitionMap如下所示。
可以看到BeanFactory
中的beanDefinitionMap多了很多BeanDefinition
,其中也包括事先定义好的TestBean
,这是因为在ConfigurationClassPostProcessor
中会将需要被加载到容器中的bean都扫描出来并创建成BeanDefinition
,然后存放到beanDefinitionMap中,但是TestBean
的构造函数中应该被打印的信息是没有被打印的,这说明ConfigurationClassPostProcessor
中只会创建BeanDefinition
并存放到beanDefinitionMap中,不会实际的实例化bean,真正的bean的实例化由AbstractApplicationContext
的finishBeanFactoryInitialization()
方法开启,这里暂不分析。
现在可知,Spring借助BeanDefinition
来创建容器中的bean,容器中的每一个bean都会由一个BeanDefinition
来描述,描述包括bean属性值,构造函数,方法,bean作用域,bean名称等信息,Spring在启动时会先扫描所有需要被加载到容器中的bean,然后为这些bean创建BeanDefinition
并添加到BeanFactory
中的beanDefinitionMap中。创建BeanDefinition
时不会实例化bean,bean的实例化在BeanDefinition
创建之后。
二. BeanDefinitionRegistry简析
Springboot启动时,会创建容器,并根据WebApplicationType
的不同,创建不同的容器,例如WebApplicationType
为SERVLET,此时使用的容器为AnnotationConfigServletWebServerApplicationContext
,如果WebApplicationType
为NONE,此时使用的容器为AnnotationConfigApplicationContext
,无论使用哪种容器,其内部持有一个BeanFactory
容器,其实际类型为DefaultListableBeanFactory
,DefaultListableBeanFactory
是一个具有注册功能的容器,因为其实现了BeanDefinitionRegistry
接口。
BeanDefinitionRegistry
接口定义了对BeanDefinition
的注册,移除和查询等操作,下面主要看一下DefaultListableBeanFactory
对BeanDefinition
的注册的实现,即registerBeanDefinition()
方法,源码如下。
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
//通过beanName将已经注册的BeanDefinition获取出来
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
//如果已经使用当前beanName注册过BeanDefinition
//则判断是否允许以相同beanName注册不同的BeanDefinition以覆盖已存在的BeanDefinition
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
//检查bean实例是否已经开始创建
if (hasBeanCreationStarted()) {
synchronized (this.beanDefinitionMap) {
//将BeanDefinition缓存到beanDefinitionMap
this.beanDefinitionMap.put(beanName, beanDefinition);
//将beanName缓存到beanDefinitionNames
List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
//从manualSingletonNames中将beanName移除,以防止beanName重复
//manualSingletonNames中缓存了手动注册的单例的名称
removeManualSingletonName(beanName);
}
}
else {//仍处于启动注册阶段
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
DefaultListableBeanFactory
实现的registerBeanDefinition()
方法中,会将beanName和BeanDefinition
以键值对的形式缓存到beanDefinitionMap中,同时还会将beanName添加到beanDefinitionNames中。由于还会存在手动注册单例bean的情况,如果手动注册了单例bean,单例bean的名称会缓存在manualSingletonNames中,所以还需要在registerBeanDefinition()
方法中保证beanDefinitionNames和manualSingletonNames中的beanName不重复。
现在做一个小节,在Springboot启动时,创建的容器中会持有一个DefaultListableBeanFactory
容器,其实现了BeanDefinitionRegistry
接口,具备对BeanDefinition
进行注册,删除和查询等功能,Springboot进行bean的扫描加载,自动装配时均会基于需要加载到容器中的bean创建BeanDefinition
,并将创建好的BeanDefinition
注册到DefaultListableBeanFactory
容器中。
总结
BeanDefinition
的主要作用是描述Spring中的bean,作用可以类比于Java
中的Class
对象,但是比Class
对象能够描述更多的bean信息,例如bean作用域,是否懒加载等。在Springboot的启动阶段,每个需要被加载到容器中的bean会被创建为一个BeanDefinition
,然后被注册到容器中,而Springboot中使用的容器均持有一个DefaultListableBeanFactory
,其实现了BeanDefinitionRegistry
接口,所以注册BeanDefinition
到容器中,实际就是注册BeanDefinition
到DefaultListableBeanFactory
中,最终每个BeanDefinition
会被缓存到DefaultListableBeanFactory
的beanDefinitionMap中。
BeanDefinition
只是一个接口,其有许多实现类,在本篇文章中并为进行分析,同时在BeanDefinition
的创建过程中还有一个重要类ConfigurationClassPostProcessor
,本篇文章也并未深入探究,上述的问题,将在后续的文章中逐一说明。