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

}
相关推荐
学长毕设2 小时前
基于SpringBoot的实验室资产管理系统
java·spring boot·后端
sbjdhjd2 小时前
网工进阶笔记:H3CSE 园区网全栈技术与协议汇总
网络·笔记·云计算·ssh·网络工程·vlan·aaa
zzu123zsw3 小时前
从臃肿的模板调用到声明式契约:深入 UniHttp 动态代理与多渠道生命周期扩展
java·spring boot·microsoft·unihttp
源码集结号4 小时前
云诊所门诊系统源码,基于SpringBoot+Vue.js架构,支持多租户设计与Docker容器化部署
java·spring boot·源码·医院信息系统·门诊系统·诊所系统
努力的小雨4 小时前
给 AI 眼镜开发应用有多难?我从零做了一个陪练,还上架了
后端
论文复现现场4 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
啃瓜子的松鼠5 小时前
泛微OAe9-后端二开异步调用
java
海绵宝宝的月光宝盒5 小时前
14-AutoCAD-阵列命令应用与案例实战(AR)
经验分享·笔记·自动化·课程设计·制造·学习方法·cad
秋天的怀念_David5 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
excel5 小时前
双 Token 与前端使用 Crypto 公私钥鉴权换 Token,哪种方案更好?
前端·后端