一、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 实例还没创建