Spring中的BeanDefinition

一、BeanDefinition概述

BeanDefinition 是 Bean 的"元数据模型",描述了一个 Bean 如何被创建和管理。

它类似于一份"对象蓝图",包含类类型、构造参数、属性注入、作用域、初始化方法等关键信息。

Spring 不会直接把一个类实例作为 Bean 放入容器,而是先构建它对应的 BeanDefinition,再由容器根据这份定义创建最终 Bean。

二、BeanDefinition的接口层次与核心方法

接口继承体系

bash 复制代码
BeanDefinition (接口)
├── AttributeAccessor (接口)
├── BeanMetadataElement (接口)
└── (主要实现类)
    ├── AbstractBeanDefinition (抽象类)
    │   ├── GenericBeanDefinition
    │   ├── RootBeanDefinition
    │   └── ChildBeanDefinition
    └── AnnotatedBeanDefinition (接口)

核心方法

java 复制代码
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

    /**
     * 标准单例作用域的标识符:"singleton"
     * 注意:扩展的bean工厂可能支持更多作用域
     */
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;

    /**
     * 标准原型作用域的标识符:"prototype"
     * 注意:扩展的bean工厂可能支持更多作用域
     */
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

    /**
     * Bean角色:应用程序主要部分
     * 通常对应于用户自定义的bean
     */
    int ROLE_APPLICATION = 0;

    /**
     * Bean角色:大型配置的支持部分
     * 通常是外部ComponentDefinition的一部分
     */
    int ROLE_SUPPORT = 1;

    /**
     * Bean角色:完全后台角色,与最终用户无关
     * 用于注册完全属于ComponentDefinition内部工作的bean
     */
    int ROLE_INFRASTRUCTURE = 2;

    // ==================== 可修改的属性 ====================

    /**
     * 设置此bean定义的父定义名称(如果有)
     */
    void setParentName(@Nullable String parentName);

    /**
     * 返回此bean定义的父定义名称(如果有)
     */
    @Nullable
    String getParentName();

    /**
     * 指定此bean定义的bean类名
     * 类名可以在bean工厂后处理期间修改
     */
    void setBeanClassName(@Nullable String beanClassName);

    /**
     * 返回此bean定义的当前bean类名
     * 注意:这不一定是运行时使用的实际类名
     */
    @Nullable
    String getBeanClassName();

    /**
     * 覆盖此bean的目标作用域,指定新的作用域名称
     * @see #SCOPE_SINGLETON
     * @see #SCOPE_PROTOTYPE
     */
    void setScope(@Nullable String scope);

    /**
     * 返回此bean的当前目标作用域名称
     */
    @Nullable
    String getScope();

    /**
     * 设置此bean是否应延迟初始化
     * 如果为false,bean将在启动时由执行单例急切初始化的bean工厂实例化
     */
    void setLazyInit(boolean lazyInit);

    /**
     * 返回此bean是否应延迟初始化
     * 仅适用于单例bean
     */
    boolean isLazyInit();

    /**
     * 设置此bean所依赖的bean的名称
     * bean工厂将保证这些bean首先被初始化
     */
    void setDependsOn(@Nullable String... dependsOn);

    /**
     * 返回此bean所依赖的bean名称
     */
    @Nullable
    String[] getDependsOn();

    /**
     * 设置此bean是否可作为自动装配候选者注入到其他bean中
     * 注意:此标志仅影响基于类型的自动装配
     */
    void setAutowireCandidate(boolean autowireCandidate);

    /**
     * 返回此bean是否可作为自动装配候选者
     */
    boolean isAutowireCandidate();

    /**
     * 设置此bean是否是主要的自动装配候选者
     * 如果在多个匹配的候选者中只有一个bean的此值为true,它将作为决胜者
     */
    void setPrimary(boolean primary);

    /**
     * 返回此bean是否是主要的自动装配候选者
     */
    boolean isPrimary();

    /**
     * 指定要使用的工厂bean名称(如果有)
     * 这是调用指定工厂方法的bean的名称
     */
    void setFactoryBeanName(@Nullable String factoryBeanName);

    /**
     * 返回工厂bean名称(如果有)
     */
    @Nullable
    String getFactoryBeanName();

    /**
     * 指定工厂方法名称(如果有)
     * 此方法将使用构造函数参数调用,如果没有指定参数则不使用参数调用
     */
    void setFactoryMethodName(@Nullable String factoryMethodName);

    /**
     * 返回工厂方法名称(如果有)
     */
    @Nullable
    String getFactoryMethodName();

    /**
     * 返回此bean的构造函数参数值
     * 返回的实例可以在bean工厂后处理期间修改
     */
    ConstructorArgumentValues getConstructorArgumentValues();

    /**
     * 返回此bean是否定义了构造函数参数值
     * @since 5.0.2
     */
    default boolean hasConstructorArgumentValues() {
        return !getConstructorArgumentValues().isEmpty();
    }

    /**
     * 返回要应用于bean新实例的属性值
     * 返回的实例可以在bean工厂后处理期间修改
     */
    MutablePropertyValues getPropertyValues();

    /**
     * 返回此bean是否定义了属性值
     * @since 5.0.2
     */
    default boolean hasPropertyValues() {
        return !getPropertyValues().isEmpty();
    }

    /**
     * 设置初始化方法名称
     * @since 5.1
     */
    void setInitMethodName(@Nullable String initMethodName);

    /**
     * 返回初始化方法名称
     * @since 5.1
     */
    @Nullable
    String getInitMethodName();

    /**
     * 设置销毁方法名称
     * @since 5.1
     */
    void setDestroyMethodName(@Nullable String destroyMethodName);

    /**
     * 返回销毁方法名称
     * @since 5.1
     */
    @Nullable
    String getDestroyMethodName();

    /**
     * 设置此BeanDefinition的角色提示
     * 角色提示为框架和工具提供了特定BeanDefinition角色和重要性的指示
     * @since 5.1
     */
    void setRole(int role);

    /**
     * 获取此BeanDefinition的角色提示
     */
    int getRole();

    /**
     * 设置此bean定义的人类可读描述
     * @since 5.1
     */
    void setDescription(@Nullable String description);

    /**
     * 返回此bean定义的人类可读描述
     */
    @Nullable
    String getDescription();

    // ==================== 只读属性 ====================

    /**
     * 返回此bean定义的可解析类型
     * 基于bean类或其他特定元数据
     * @since 5.2
     */
    ResolvableType getResolvableType();

    /**
     * 返回是否是单例(Singleton)
     * 所有调用都返回同一个共享实例
     */
    boolean isSingleton();

    /**
     * 返回是否是原型(Prototype)
     * 每次调用都返回独立的实例
     * @since 3.0
     */
    boolean isPrototype();

    /**
     * 返回此bean是否是"抽象"的
     * 抽象bean不意味着被实例化
     */
    boolean isAbstract();

    /**
     * 返回此bean定义来源的资源描述
     * (用于在出错时显示上下文)
     */
    @Nullable
    String getResourceDescription();

    /**
     * 返回原始的BeanDefinition(如果有)
     * 允许检索装饰的bean定义(如果有)
     * 注意:此方法返回直接原始定义。迭代原始定义链可以找到用户定义的原始BeanDefinition
     */
    @Nullable
    BeanDefinition getOriginatingBeanDefinition();
}

三、BeanDefinition的实现类及属性

  • AttributeAccessor:最顶层接口。赋予了 BeanDefinition 保存任意元数据(Metadata)的能力。它就像一个 Map,你可以往里塞任何自定义的属性(主要用于插件或框架扩展)。
  • BeanMetadataElement:另一个父接口,用于存放Bean的元信息,提供 getSource() 方法,用于获取Bean 定义的来源(比如来自哪个 XML 文件或哪个配置类),方便报错时定位。
  • AbstractBeanDefinition:这是所有具体实现类的基类。它实现了 BeanDefinition 接口的大部分方法,定义了通用的属性结构。

3.1核心属性

1)beanClass / beanClassName(Bean的Class对象)

表示 Bean 的类型

如果是通过 @Bean、factory-method 等方式创建,则 class 可能无法提前确定

2)scope(作用域)

singleton、prototype、request、session、application

典型用法:@Scope("prototype")

3)lazyInit(是否延迟初始化)

  • true:容器启动不初始化
  • false:容器启动时立即创建(对于 singleton 生效)

4) propertyValues(属性值)

它是一个键值对集合,如果你在 XML 写了 ,或者在 BeanDefinition 中手动添加了属性,都会存在这里。Spring 在实例化对象后,会遍历这个集合进行属性填充(Populate Bean)

5)dependsOn(依赖的Bean名称)

声明 Bean 之间的依赖顺序。

6) autowireCandidate(控制是否参与自动注入)

7) primary(配置多实例时区分主 bean)

3.2核心实现类

RootBeanDefinition

RootBeanDefinition表示一个完整的、独立的Bean定义,可以作为其他Bean定义的父定义。

  • Spring 内部使用最频繁
  • 通常表示最终可用的"合并后 BeanDefinition"
  • 甚至包含了解析后的 beanClass(已经加载成 Class 对象)

特点:

  • 当出现父子 BeanDefinition 继承时,最终都会合并为 RootBeanDefinition
  • 运行期间 BeanFactory 主要使用它

GenericBeanDefinition

这是 Spring 2.5 以后推荐使用的通用实现类。取代了原来的RootBeanDefinition和ChildBeanDefinition组合。

  • 既可以动态设置父 Bean,又可以配置所有的属性。
  • 非最终版本,状态比较"原始"
  • 允许灵活设置各类属性
  • 如果你通过源码手动注册一个 Bean,通常使用这个类。Spring 内部处理 XML 配置时,也会将其转换为 GenericBeanDefinition。

ChildBeanDefinition

ChildBeanDefinition允许子Bean定义从父定义中继承配置,实现配置的复用。早期 XML 时代支持父子 Bean 继承,大多数现代项目已较少使用

AnnotatedBeanDefinition

处理注解驱动的Bean定义,特别是通过@Component、@Service等注解定义的Bean。

特点:它们实现了 AnnotatedBeanDefinition 接口,内部持有一个 AnnotationMetadata,能够获取类上的注解信息(比如 @Scope("prototype") 中的值)

四、BeanDefinition的创建与注册流程

整体流程概览

  • 资源定位:找到 XML/注解配置所在位置
  • 读取配置:将配置文件读取为 Document 或扫描的元数据
  • 解析配置:将 bean 信息解析出来
  • 构建 BeanDefinition:根据解析结果创建 "Bean 蓝图"
  • 生成 beanName:根据规则确定唯一名称
  • 注册 BeanDefinition:放入 BeanFactory 中的注册表

使用 @Configuration + @Bean

java 复制代码
@Configuration
public class AppConfig {
    @Bean
    public MyService myService() {
        return new MyService();
    }
}

流程:

  • AnnotationConfigApplicationContext 启动时,通过 AnnotatedBeanDefinitionReader 读取 AppConfig.class
  • 将 AppConfig 自身注册为一个 BeanDefinition
  • 在后续的 ConfigurationClassPostProcessor 阶段,解析其中的 @Bean 方法,为每个 @Bean 方法生成额外的 BeanDefinition
  • 所有 BeanDefinition 注册到 BeanFactory

核心类:AnnotatedBeanDefinitionReader + ConfigurationClassPostProcessor

使用 @Component(或 @Service 等)

java 复制代码
@Component
public class UserService { }

流程:

  • 如果主配置类上有 @ComponentScan,Spring 会启动 ClassPathBeanDefinitionScanner
    扫描指定包下的所有类,找出带 @Component 注解的类
  • 为每个匹配类创建 ScannedGenericBeanDefinition
  • 自动生成 beanName(如 userService)
  • 注册到 BeanFactory

核心类:ClassPathBeanDefinitionScanner

使用 XML 配置文件(传统方式)

java 复制代码
<bean id="orderService" class="com.example.OrderService">
    <property name="userService" ref="userService"/>
</bean>

流程简述:

  • 容器(如 ClassPathXmlApplicationContext)加载 XML 文件
  • 通过 XmlBeanDefinitionReader 解析 标签
  • 为每个 创建 GenericBeanDefinition,提取 class、scope、property 等信息
  • 以 id(或 name)作为 beanName,调用 registerBeanDefinition() 注册到 BeanFactory

核心类:XmlBeanDefinitionReader、BeanDefinitionParserDelegate、DefaultBeanDefinitionDocumentReader

注册 BeanDefinition

Spring 定义了一个核心接口:

java 复制代码
public interface BeanDefinitionRegistry {
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;
    
    void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    
    boolean containsBeanDefinition(String beanName);
    
    // ...
}

实现类:DefaultListableBeanFactory

  • 这是 Spring 默认使用的 BeanFactory 实现
  • 它同时实现了 BeanFactory 和 BeanDefinitionRegistry
  • 所有标准的 ApplicationContext(如 AnnotationConfigApplicationContext、ClassPathXmlApplicationContext)底层都使用它来存储 BeanDefinition

所以,注册 = 调用 DefaultListableBeanFactory.registerBeanDefinition(...)

注册时做了什么?

当调用 registerBeanDefinition(beanName, beanDefinition) 时,容器会:

  • 校验参数合法性:beanName 不能为 null、beanDefinition 不能为 null

  • 检查是否已存在同名 BeanDefinition,如果存在:若允许覆盖(allowBeanDefinitionOverriding=true,默认 true),则替换,否则抛出 BeanDefinitionOverrideException

  • 将 BeanDefinition 存入内部 Map

    • this.beanDefinitionMap.put(beanName, beanDefinition);
  • 将 beanName 加入 bean 名称列表

    • this.beanDefinitionNames.add(beanName);

BeanDefinition 被存入 beanDefinitionMap 之后,Spring 容器完成了"声明式配置的解析与注册"阶段,此时Bean 实例还没创建

相关推荐
编程修仙1 小时前
第十篇 文件上传
spring boot·spring
不会代码的小猴1 小时前
C++的第十四天笔记
java·开发语言
Heavydrink1 小时前
Java项目部署云服务器详细教程
java·服务器·开发语言
楠枬1 小时前
负载均衡 -LoadBalance
后端·spring·spring cloud·负载均衡
milanyangbo1 小时前
深入解析 Disruptor:从RingBuffer到缓存行填充的底层魔法
java·数据库·后端·架构
yaoxin5211231 小时前
266. Java 集合 - ArrayList vs LinkedList 内存使用深度剖析
java·开发语言
没有bug.的程序员2 小时前
云原生 + JIT:冷启动与预热优化
java·jvm·云原生·日志·gc·jit
计算机学姐2 小时前
基于Python的智能点餐系统【2026最新】
开发语言·vue.js·后端·python·mysql·django·flask
risc1234562 小时前
【备忘录】java.lang.Throwable#addSuppressed这个是干嘛的?
java·开发语言