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

}
相关推荐
步行cgn1 小时前
Spring 通过 factory-method 实例化 Bean 详解与常见错误排查
java·后端
lv__pf1 小时前
seata【实战 msb】
java·开发语言·后端
Ricon组态薄荷糖1 小时前
Ricon组态系统:工业组件开发指南与实践
前端·后端·物联网
旺仔不是程序员1 小时前
相关子查询与性能优化:PostgreSQL 逐行执行的代价与 JOIN 重写
数据库·后端·sql
右耳朵猫AI2 小时前
PHP周刊2026W38 | Laravel AI可观测、Symfony三版本、Webhook零信任、事件溯源架构
后端·php·laravel
沙蒿同学2 小时前
我把架构约定编译成了会变红的测试:Wails v2 + Go + Vue3 桌面脚手架实战
前端·后端·github
cxoptics2 小时前
偏振片(起偏器/检偏器)怎么区分?
java
量化分析码农2 小时前
【Python量化数据工程实战 #01】拉下来的行情全是"脏数据"?停牌、跳空、异常值一站式清洗
后端
专业程序开发源2 小时前
springboot篮球联赛管理系统13635-计算机课程设计、毕业设计
vue.js·spring boot·后端·python·django·php·课程设计
StevenLdh2 小时前
第四期 · 云端同步与冲突仲裁:离线可写、多端一致的工程解法
java·微信小程序