《Spring Boot 3:高级与架构设计》第 1 章 BeanDefinitionRegistry 阅读笔记 3
1.4 BeanDefinitionRegistry
BeanDefinition 是一个描述 Bean 的信息载体。从 DefaultListableBeanFactory 中获取 BeanDefinition,跟另一个接口有关:BeanDefinitionRegistry。
1.4.1 BeanDefinitionRegistry 概述
首先,简单了解 BeanDefinitionRegistry 的设计构造。javadoc 和源码如图所示。
BeanDefinitionRegistry 是一个接口,包含 BeanDefinition 的注册表信息。

可以使用此接口的实现类来操作 BeanDefinition。Spring Framework 中的已知实现类是 DefaultListableBeanFactory 和GenericApplicationContext。

1. BeanDefinitionRegistry 中存放了所有 BeanDefinition
Registry 有注册表的含义,可类比 Windows 的注册表---存放了 Windows 系统中的应用和设置信息。沿此思路,BeanDefinitionRegistry 中存放的就应该是 BeanDefinition 的设置信息。其实 Spring Framework 底层对于 BeanDefinition 的注册表的设计,本质上就是一个 Map,如代码所示。

2. BeanDefinitionRegistry 中维护了 BeanDefinition
Registry 还有注册器的含义。既然 Map 有基本的增删改查操作,那作为 BeanDefinition 的注册器,自然也就有 BeanDefinition 的注册功能。BeanDefinitionRegistry 中有 3 个方法,刚好对应了 BeanDefinition 的增、删、查,如代码所示。

3. BeanDefinitionRegistry 支撑其他组件运行
笔者尝试用一个例子来引导读者理解 BeanDefinitionRegistry 的支撑作用。
入门篇的 4.4.6 节讲解 ImportBeanDefinitionRegistrar 时,操纵接口方法 registerBeanDefinitions 中第 2 个参数 BeanDefinitionRegistry 的 API 完成了 "服务员" 类的注册,所以在这个过程中,BeanDefinitionRegistry 会支撑 ImportBeanDefinitionRegistrar 完成它的 BeanDefinition 注册工作。BeanDefinition 注册到 IOC 容器后,由 IOC 容器负责将 BeanDefinition 转换为对应的 bean 对象。
入门篇的 4.4.6 节 https://blog.csdn.net/Rockandrollman/article/details/163914091
4. BeanDefinitionRegistry 的主要实现是 DefaultListableBeanFactory
可能部分读者借助 IDE 发现很多 ApplicationContext 也实现了 BeanDefinitionRegistry,所以认为 ApplicationContext 也能管理 BeanDefinition。需要区分的是,ApplicationContext 本身不负责管理 BeanDefinition,而是通过内部依赖的 DefaultListableBeanFactory 来实现,所以 ApplicationContext 对 BeanDefinition 的操作也转交给内部的 DefaultListableBeanFactory 来实现。


1.4.2 注册与移除
BeanDefinitionRegistry 的源码不用去分析,主要目标是掌握如何利用 BeanDefinitionRegistry 去维护 BeanDefinition。
1. 注册 BeanDefinition
目前接触的注册 BeanDefinition 的方式就是借助 ImportBeanDefinitionRegistrar 接口,读者可以回看入门篇的 4.4.6 节。
入门篇的 4.4.6 节 https://blog.csdn.net/Rockandrollman/article/details/163914091
2. 移除 BeanDefinition
BeanDefinitionRegistry 除了能给 IOC 容器中添加 BeanDefinition,还可以移除一些特定的 BeanDefinition。移除操作会在 Bean 的实例化之前进行,目的是阻止 IOC 容器创建对应的 Bean。
(1)声明 Person
如代码所示。

java
package com.yangjunbo.spring.definition.examplec;
/**
* ClassName: Person
* Package: com.yangjunbo.spring.definition.examplec
* Description:
*
* @Author 杨钧博
* @Create 2026/9/7 22:35
* @Version 1.0
*/
public class Person {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
(2)编写配置文件
接下来要注册两个 Person 对象,分别是一个成年人和一个儿童。由 1.3.3 节最后总结的 BeanDefinition 注册方式与实现类型可知,如果此处使用注解配置类的方式注册 Bean(借助 @Bean),生成的 BeanDefinition 将无法获取 beanClassName(也无法获取 PropertyValues),故此处选用 XML 配置文件的方式注册 Bean,如代码所示。

xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="chengnianren" class="com.yangjunbo.spring.definition.examplec.bean.Person">
<property name="name" value="chengnianren"/>
<property name="age" value="18"/>
</bean>
<bean id="ertong" class="com.yangjunbo.spring.definition.examplec.bean.Person">
<property name="name" value="ertong"/>
<property name="age" value="8"/>
</bean>
</beans>
(3)编写移除 BeanDefinition 的组件
移除 BeanDefinition 时需要用到后置处理器组件 BeanFactoryPostProcessor,这个组件可以在 XML 配置文件和注解配置类都加载到 IOC 容器后修改 IOC 容器中某些 BeanDefinition 的属性,抑或是直接移除 BeanDefinition。
为此,可以编写一个 RemoveBeanDefinitionPostProcessor,实现 BeanFactoryPostProcessor 接口,并重写必要的 postProcessBeanFactory方法。
注意该方法的入参是一个 ConfigurableListableBeanFactory,毫无疑问,它的唯一实现是 DefaultListableBeanFactory,而DefaultListableBeanFactory 本身又实现了 BeanDefinitionRegistry 接口,所以相当于可以直接将 beanFactory 强转为 BeanDefinitionRegistry 类型。得到 BeanDefinitionRegistry 后就可以假定一个移除规则:小于 18 岁的 Person 会被移除。编写方式如代码所示。


java
package com.yangjunbo.spring.definition.examplec.config;
import com.yangjunbo.spring.definition.examplec.bean.Person;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.stereotype.Component;
@Component
public class RemoveBeanDefinitionPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// 获取IOC容器中的所有BeanDefinition
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
// 判断BeanDefinition对应的Bean是否为Person类型
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);
if (Person.class.getName().equals(beanDefinition.getBeanClassName())) {
// 判断Person的年龄是否大于等于18,小于18的会被移除
TypedStringValue age = (TypedStringValue) beanDefinition.getPropertyValues().get("age");
if (Integer.parseInt(age.getValue()) < 18) {
// 移除BeanDefinition
registry.removeBeanDefinition(beanDefinitionName);
}
}
}
}
}

xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="chengnianren" class="com.yangjunbo.spring.definition.examplec.bean.Person">
<property name="name" value="chengnianren"/>
<property name="age" value="18"/>
</bean>
<bean id="ertong" class="com.yangjunbo.spring.definition.examplec.bean.Person">
<property name="name" value="ertong"/>
<property name="age" value="8"/>
</bean>
<!-- 注意此处要开启组件扫描 -->
<context:component-scan base-package="com.yangjunbo.spring.definition.examplec.config"/>
</beans>
(4)测试获取 "小明"
使用 XML 配置文件驱动 IOC 容器,随后从 IOC 容器中尝试获取 chengnianren 和 ertong,如代码所示。运行 main 方法之后可以发现可以正常获取 chengnianren,但是获取 ertong 时会打印 NoSuchBeanDefinitionException,这说明 ertong 对应的 BeanDefinition 已经被移除,无法创建对应的 Person 实例。

java
package com.yangjunbo.spring.definition.examplec;
import com.yangjunbo.spring.definition.examplec.bean.Person;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* ClassName: ExampleCApplication
* Package: com.yangjunbo.spring.definition.examplec
* Description:
*
* @Author 杨钧博
* @Create 2026/9/7 22:59
* @Version 1.0
*/
public class ExampleCApplication {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("examplec/spring-examplec.xml");
Person dazhuang = (Person) ctx.getBean("chengnianren");
System.out.println(dazhuang);
Person xiaoming = (Person) ctx.getBean("ertong");
System.out.println(xiaoming);
}
}
我的理解,
BeanDefinitionRegistry 的作用是操作 IOC 容器的 BeanDefinition,IOC 容器内部会根据 BeanDefinition 创建 Bean。
1.5 小结
本章从 "元" 这个概念开始,逐一讲解元编程、元信息、配置元信息等多个复杂概念。这些概念对后续理解和深入学习 IOC 容器的原理有重大帮助。
对于 IOC 容器的 Bean 而言,BeanDefinition 中定义了 Bean 的元信息,其中记录了包括 Bean 的类型、id、作用域、属性赋值和依赖注入等多方面信息。
IOC 容器利用 BeanDefinition 即可解析并生成相应的 Bean。
IOC 容器中的 Bean 集中保存在 BeanDefinitionRegistry 的实现类 DefaultListableBeanFactory 中。BeanDefinitionRegistry 的设计与容器类似,都有保存、获取、移除动作。借助 BeanDefinitionRegistry 可以在程序中动态添加新的 BeanDefinition,也可以在 IOC 容器没有集中创建 Bean之前,修改 BeanDefinition 的信息甚至移除 BeanDefinition。