《Spring Boot 3:高级与架构设计》第 1 章 BeanDefinitionRegistry 个人理解 3

《Spring Boot 3:高级与架构设计》第 1 章 BeanDefinitionRegistry 个人理解 3

1 什么是 BeanDefinitionRegistry

BeanDefinition 是 Bean 的元信息,即 Bean 的定义信息。

BeanDefinitionRegistry 是 BeanDefinition 的注册表,负责保存和维护 BeanDefinition 的信息。

2 BeanDefinitionRegistry 中存放了所有 BeanDefinition 的信息

BeanDefinitionRegistry 是一个接口,DefaultListableBeanFactory 是实现类。

Spring Framework 底层对于 BeanDefinition 的注册表的设计,本质上就是一个 Map,如代码所示。

3 BeanDefinitionRegistry 可以维护 BeanDefinition 的信息

BeanDefinitionRegistry 中有 3 个方法,刚好对应了 BeanDefinition 的增、删、查,如代码所示。

4 BeanDefinitionRegistry 的作用

BeanDefinitionRegistry 可以向 IOC 容器中注入 BeanDefinition,IOC 容器再将 BeanDefinition 转为了 Bean。

4.4.6 节,使用 BeanDefinitionRegistry 的案例

5 DefaultListableBeanFactory 是主要实现类

DefaultListableBeanFactory 是 BeanDefinitionRegistry 的主要实现类,

GenericApplicationContext 也是 BeanDefinition 的实现类,但是内部依赖了 DefaultListableBeanFactory 进行 BeanDefinition 的管理。

6 使用 BeanDefinitionRegistry 注册 BeanDefinition

4.4.6 节,使用 BeanDefinitionRegistry 的案例

7 实现 BeanDefinitionRegistry 移除 BeanDefinition

移除 BeanDefinition 的操作会在 Bean 的实例化之前进行,目的是阻止 IOC 容器创建对应的 Bean。

7.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 +
                '}';
    }

}

7.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>

7.3 编写移除 BeanDefinition 的组件

移除 BeanDefinition 时需要用到后置处理器组件 BeanFactoryPostProcessor,这个组件可以在 XML 配置文件或注解配置类加载到 IOC 容器后修改 IOC 容器中的 BeanDefinition。

为此,可以编写一个 RemoveBeanDefinitionPostProcessor,实现 BeanFactoryPostProcessor 接口,并重写必要的 postProcessBeanFactory方法。

注意该方法的入参是一个 ConfigurableListableBeanFactory,毫无疑问,它的唯一实现是 DefaultListableBeanFactory。

DefaultListableBeanFactory 本身又实现了 BeanDefinitionRegistry 接口。

所以相当于可以直接将 ConfigurableListableBeanFactory 强转为 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>

7.4 测试

使用 XML 配置文件驱动 IOC 容器,随后从 IOC 容器中尝试获取 chengnianren 和 ertong,如代码所示。运行 main 方法之后可以发现可以正常获取 chengnianren,但是获取 ertong 时会打印 NoSuchBeanDefinitionException,这说明 ertong 对应的 BeanDefinition 已经被移除,无法创建对应的 Bean。

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);
    }

}
相关推荐
EatFan7 小时前
2026 Rust 后端技术栈选型:Axum 0.8 + Tokio + SQLx 全链路怎么搭
开发语言·后端·rust·tokio·serde·axum·sqlx
2401_888859717 小时前
STM32H733 MPU、AXI、FMC学习
java·开发语言·stm32·spring
深圳老胡7 小时前
STM32F4 OTA升级:单App与双App方案优缺点对比
笔记·stm32·单片机·代码规范
mldong7 小时前
审批详情页高亮的那个节点,"名字"其实是一串 id:jeeflow 工作流引擎的四块数据来源
后端·架构
高频因子挖掘机7 小时前
股票历史数据缺了一天怎么发现?用 Pandas 检查交易日期连续性
后端·github·api
高频因子挖掘机7 小时前
量化回测最危险的错误,不是程序报错,而是数据错了却正常运行
后端·github·api
云浪8 小时前
Go 堆实战:从完全二叉树到 Top-K 与任务调度
后端·go
CV工程师丁Sir9 小时前
ArkWeb 手记 04|DevTools 调试与缓存清理
java·spring·缓存·harmonyos
光依旧15 小时前
MCP实战手记系列(十):MCP Server高可用实战
spring boot·高可用·nginx负载均衡·redis哨兵·mcp·微服务容错·resilience4j熔断
Sarvartha15 小时前
final 关键字
java·开发语言