开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 17 天,点击查看活动详情
前言
BeanDefinition是Spring中的十分重要的一个类,所有bean的前生都是BeanDefinition,学习Springboot和Spring时,会经常遇到该类,所以本篇文章会对BeanDefinition的概念进行入门学习,同时一并学习定义注册,移除和查询BeanDefinition的BeanDefinitionRegistry接口。
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。
示例工程中在配置类中定义了一个bean,如下所示。
@Service
public class MyService {
public MyService() {
System.out.println("MyService init.");
}
}
@Configuration
public class MyConfig {
@Bean
public MyService myService() {
return new MyService();
}
}
已知在初始化容器时,初始化逻辑在AbstractApplicationContext的refresh() 方法中,如下所示。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 在这里调用到ConfigurationClassPostProcessor的逻辑
// 将需要注册到容器中的bean加载为BeanDefinition并存放到BeanFactory的beanDefinitionMap中
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
// 初始化bean
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
// 省略
}
finally {
resetCommonCaches();
contextRefresh.end();
}
}
}
现在将断点打到AbstractApplicationContext的refresh() 方法中调用invokeBeanFactoryPostProcessors() 方法的这一行代码,此时观察BeanFactory中的beanDefinitionMap如下所示。
此时往后执行一步,再观察BeanFactory中的beanDefinitionMap如下所示。
可以看到BeanFactory中的beanDefinitionMap多了事先定义好的MyService的BeanDefinition,这是因为在ConfigurationClassPostProcessor中会将需要被加载到容器中的bean都扫描出来并创建成BeanDefinition,然后存放到beanDefinitionMap中,但是MyService的构造函数中应该被打印的信息是没有被打印的,这说明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简析
Spring中会根据配置的不同使用不同的容器,但无论使用哪种容器,其内部都会持有一个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<String> 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不重复。
现在做一个小节,在Spring启动时,创建的容器中会持有一个DefaultListableBeanFactory容器,其实现了BeanDefinitionRegistry接口,具备对BeanDefinition进行注册,删除和查询等功能,Spring进行bean的扫描加载时均会基于需要加载到容器中的bean创建BeanDefinition,并将创建好的BeanDefinition注册到DefaultListableBeanFactory容器中。
总结
BeanDefinition的主要作用是描述Spring中的bean,作用可以类比于Java中的Class对象,但是比Class对象能够描述更多的bean信息,例如bean作用域,是否懒加载等。在Spring的启动阶段,每个需要被加载到容器中的bean会被创建为一个BeanDefinition,然后被注册到容器中,而Spring中使用的容器均持有一个DefaultListableBeanFactory,其实现了BeanDefinitionRegistry接口,所以注册BeanDefinition到容器中,实际就是注册BeanDefinition到DefaultListableBeanFactory中,最终每个BeanDefinition会被缓存到DefaultListableBeanFactory的beanDefinitionMap中。
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 17 天,点击查看活动详情