BeanDefinition 是如何被 Spring 容器加载和解析的?
·
要理解 BeanDefinition 被 Spring 容器加载和解析的过程,核心是搞清楚:Spring 如何把我们写的配置(XML / 注解 / JavaConfig)转换成 BeanDefinition,再注册到容器中。
下面我会从「核心流程」「不同配置方式的解析细节」「关键组件」三个维度,由浅入深地拆解这个过程,结合代码和示例让你一目了然。
一、BeanDefinition 加载解析的核心流程
不管是 XML 配置还是注解配置,Spring 解析 BeanDefinition 的核心流程都是通用的,可概括为 5 步:

graph TD
A[启动容器] --> B[加载配置资源]
B --> C[解析配置为 BeanDefinition]
C --> D[注册 BeanDefinition 到注册表]
D --> E[合并 BeanDefinition(父子Bean)]
E --> F[容器刷新:根据BeanDefinition创建Bean]
关键说明:
- 加载配置资源:Spring 先定位配置文件 / 类(如
applicationContext.xml、@Configuration类),将其封装为Resource资源对象; - 解析为 BeanDefinition:通过专门的解析器(如 XML 解析器、注解解析器),把配置中的
<bean>标签 /@Component注解等转换成BeanDefinition对象; - 注册到注册表:将解析好的
BeanDefinition注册到BeanDefinitionRegistry(核心实现是DefaultListableBeanFactory),注册表本质是一个Map<String, BeanDefinition>,key 是 Bean 名称,value 是 BeanDefinition; - 合并 BeanDefinition:如果有父子 Bean(如
<bean parent="xxx">),会合并父子 BeanDefinition 的属性,最终生成一个完整的 BeanDefinition; - 创建 Bean:容器刷新阶段,根据最终的 BeanDefinition 反射创建 Bean 实例。
二、不同配置方式的解析细节(实战拆解)
Spring 支持多种配置方式,不同方式的解析逻辑略有差异,下面重点讲最常用的「XML 配置」和「注解配置」。
1. XML 配置的解析过程(经典方式)
以 <bean id="user" class="com.example.User"/> 为例,拆解解析步骤:
核心组件:
XmlBeanDefinitionReader:专门负责读取 XML 配置并解析为 BeanDefinition;BeanDefinitionParserDelegate:辅助解析<bean>标签的核心类,负责解析class、scope、init-method等属性。
代码示例(手动模拟 XML 解析过程):
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.ClassPathResource;
public class XmlBeanDefinitionParseDemo {
public static void main(String[] args) {
// 1. 创建通用 Spring 容器
GenericApplicationContext context = new GenericApplicationContext();
// 2. 创建 XML 解析器,绑定到容器(注册表)
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
// 3. 加载并解析 XML 配置文件(核心步骤)
// loadBeanDefinitions 方法会完成:加载资源 → 解析标签 → 生成 BeanDefinition → 注册
int count = reader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
System.out.println("解析并注册了 " + count + " 个 BeanDefinition");
// 4. 刷新容器,创建 Bean 实例
context.refresh();
// 5. 获取 Bean(验证解析成功)
User user = context.getBean(User.class);
System.out.println(user);
context.close();
}
}
底层解析逻辑(关键步骤):
XmlBeanDefinitionReader读取applicationContext.xml,通过 DOM 解析器解析 XML 文档;- 遍历
<beans>下的<bean>标签,调用BeanDefinitionParserDelegate.parseBeanDefinitionElement(ele)方法; - 解析
<bean>标签的id(Bean 名称)、class(全类名)、scope等属性,封装为GenericBeanDefinition; - 解析
<property>标签(属性注入),将属性值存入BeanDefinition的propertyValues; - 调用
BeanDefinitionRegistry.registerBeanDefinition(beanName, bd)完成注册。
2. 注解配置的解析过程(主流方式)
以 @Component/@Service/@Configuration 为例,核心是「包扫描 + 注解解析」。
核心组件:
ClassPathBeanDefinitionScanner:包扫描器,负责扫描指定包下的类,解析注解生成 BeanDefinition;AnnotatedBeanDefinitionReader:手动注册注解 Bean 时使用(如register(User.class));AnnotationConfigApplicationContext:注解驱动的容器,内置了扫描和解析逻辑。
代码示例(自动扫描解析):
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
// 配置类:指定扫描包
@org.springframework.context.annotation.Configuration
@ComponentScan("com.example") // 扫描 com.example 下的所有 @Component 注解类
public class AnnotationBeanDefinitionParseDemo {
public static void main(String[] args) {
// 1. 创建注解容器,传入配置类(会自动扫描指定包)
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AnnotationBeanDefinitionParseDemo.class);
// 2. 容器启动时已完成:扫描 → 解析注解 → 注册 BeanDefinition → 创建 Bean
// 3. 获取 Bean(验证)
UserService userService = context.getBean(UserService.class);
System.out.println(userService);
context.close();
}
}
// 被扫描的 Bean 类
@Component
public class UserService {
// ...
}
底层解析逻辑(关键步骤):
ClassPathBeanDefinitionScanner扫描@ComponentScan指定的包,遍历包下所有.class文件;- 对每个类,通过
AnnotationMetadata解析类上的注解(如@Component/@Service/@Scope); - 若类标注了
@Component及其派生注解(@Service/@Repository/@Controller),则创建AnnotatedGenericBeanDefinition; - 解析注解属性:如
@Scope("prototype")设为 BeanDefinition 的 scope,@Value("张三")设为 propertyValues; - 生成 Bean 名称(默认是类名首字母小写,如
UserService→userService),注册到BeanDefinitionRegistry。
三、关键补充:核心接口 / 类总结
| 组件 | 作用 |
|---|---|
BeanDefinitionRegistry | BeanDefinition 注册表接口,定义了注册 / 删除 / 获取 BeanDefinition 的方法 |
DefaultListableBeanFactory | 核心实现类,既是 BeanFactory 也是 BeanDefinitionRegistry,内置 Map 存储 BeanDefinition |
BeanDefinitionReader | 读取配置资源并解析为 BeanDefinition 的顶级接口 |
XmlBeanDefinitionReader | 解析 XML 配置的实现类 |
AnnotatedBeanDefinitionReader | 解析注解 Bean 的实现类(手动注册) |
ClassPathBeanDefinitionScanner | 扫描包下注解类并解析为 BeanDefinition 的扫描器 |
四、特殊场景:JavaConfig 解析(@Bean 注解)
除了 @Component,@Configuration 类中的 @Bean 方法也会被解析为 BeanDefinition,逻辑如下:
- 扫描到
@Configuration类后,Spring 会解析类中的@Bean方法; - 为每个
@Bean方法创建ConfigurationClassBeanDefinition(BeanDefinition 的子类); beanClassName不是直接指向返回值类型,而是指向配置类,factoryMethodName设为@Bean方法名;- 容器创建 Bean 时,会通过「配置类实例 + 工厂方法」创建(而非直接反射构造器)。
示例:
@Configuration
public class AppConfig {
@Bean // 解析为 BeanDefinition,factoryMethodName = "user",factoryBeanName = "appConfig"
public User user() {
return new User();
}
}
总结
- 核心逻辑:Spring 容器启动时,先通过「解析器 / 扫描器」将 XML / 注解 / JavaConfig 配置转换为 BeanDefinition,注册到注册表,最终根据 BeanDefinition 创建 Bean;
- 关键组件:XML 解析用
XmlBeanDefinitionReader,注解扫描用ClassPathBeanDefinitionScanner,注册表核心是DefaultListableBeanFactory; - 核心差异:XML 解析是「解析标签属性」,注解解析是「扫描类 + 解析注解元信息」,但最终都落地为 BeanDefinition。
理解这个过程,就能搞懂 Spring 容器「如何识别 Bean」「如何存储 Bean 定义」,也是理解 Spring 依赖注入、Bean 生命周期的基础。
更多推荐
所有评论(0)