要理解 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]
关键说明:
  1. 加载配置资源:Spring 先定位配置文件 / 类(如 applicationContext.xml@Configuration 类),将其封装为 Resource 资源对象;
  2. 解析为 BeanDefinition:通过专门的解析器(如 XML 解析器、注解解析器),把配置中的 <bean> 标签 /@Component 注解等转换成 BeanDefinition 对象;
  3. 注册到注册表:将解析好的 BeanDefinition 注册到 BeanDefinitionRegistry(核心实现是 DefaultListableBeanFactory),注册表本质是一个 Map<String, BeanDefinition>,key 是 Bean 名称,value 是 BeanDefinition;
  4. 合并 BeanDefinition:如果有父子 Bean(如 <bean parent="xxx">),会合并父子 BeanDefinition 的属性,最终生成一个完整的 BeanDefinition;
  5. 创建 Bean:容器刷新阶段,根据最终的 BeanDefinition 反射创建 Bean 实例。

二、不同配置方式的解析细节(实战拆解)

Spring 支持多种配置方式,不同方式的解析逻辑略有差异,下面重点讲最常用的「XML 配置」和「注解配置」。

1. XML 配置的解析过程(经典方式)

<bean id="user" class="com.example.User"/> 为例,拆解解析步骤:

核心组件:
  • XmlBeanDefinitionReader:专门负责读取 XML 配置并解析为 BeanDefinition;
  • BeanDefinitionParserDelegate:辅助解析 <bean> 标签的核心类,负责解析 classscopeinit-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();
    }
}
底层解析逻辑(关键步骤):
  1. XmlBeanDefinitionReader 读取 applicationContext.xml,通过 DOM 解析器解析 XML 文档;
  2. 遍历 <beans> 下的 <bean> 标签,调用 BeanDefinitionParserDelegate.parseBeanDefinitionElement(ele) 方法;
  3. 解析 <bean> 标签的 id(Bean 名称)、class(全类名)、scope 等属性,封装为 GenericBeanDefinition
  4. 解析 <property> 标签(属性注入),将属性值存入 BeanDefinitionpropertyValues
  5. 调用 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 {
    // ...
}
底层解析逻辑(关键步骤):
  1. ClassPathBeanDefinitionScanner 扫描 @ComponentScan 指定的包,遍历包下所有 .class 文件;
  2. 对每个类,通过 AnnotationMetadata 解析类上的注解(如 @Component/@Service/@Scope);
  3. 若类标注了 @Component 及其派生注解(@Service/@Repository/@Controller),则创建 AnnotatedGenericBeanDefinition
  4. 解析注解属性:如 @Scope("prototype") 设为 BeanDefinition 的 scope,@Value("张三") 设为 propertyValues;
  5. 生成 Bean 名称(默认是类名首字母小写,如 UserServiceuserService),注册到 BeanDefinitionRegistry

三、关键补充:核心接口 / 类总结

组件作用
BeanDefinitionRegistryBeanDefinition 注册表接口,定义了注册 / 删除 / 获取 BeanDefinition 的方法
DefaultListableBeanFactory核心实现类,既是 BeanFactory 也是 BeanDefinitionRegistry,内置 Map 存储 BeanDefinition
BeanDefinitionReader读取配置资源并解析为 BeanDefinition 的顶级接口
XmlBeanDefinitionReader解析 XML 配置的实现类
AnnotatedBeanDefinitionReader解析注解 Bean 的实现类(手动注册)
ClassPathBeanDefinitionScanner扫描包下注解类并解析为 BeanDefinition 的扫描器

四、特殊场景:JavaConfig 解析(@Bean 注解)

除了 @Component@Configuration 类中的 @Bean 方法也会被解析为 BeanDefinition,逻辑如下:

  1. 扫描到 @Configuration 类后,Spring 会解析类中的 @Bean 方法;
  2. 为每个 @Bean 方法创建 ConfigurationClassBeanDefinition(BeanDefinition 的子类);
  3. beanClassName 不是直接指向返回值类型,而是指向配置类,factoryMethodName 设为 @Bean 方法名;
  4. 容器创建 Bean 时,会通过「配置类实例 + 工厂方法」创建(而非直接反射构造器)。

示例:

@Configuration
public class AppConfig {
    @Bean // 解析为 BeanDefinition,factoryMethodName = "user",factoryBeanName = "appConfig"
    public User user() {
        return new User();
    }
}

总结

  1. 核心逻辑:Spring 容器启动时,先通过「解析器 / 扫描器」将 XML / 注解 / JavaConfig 配置转换为 BeanDefinition,注册到注册表,最终根据 BeanDefinition 创建 Bean;
  2. 关键组件:XML 解析用 XmlBeanDefinitionReader,注解扫描用 ClassPathBeanDefinitionScanner,注册表核心是 DefaultListableBeanFactory
  3. 核心差异:XML 解析是「解析标签属性」,注解解析是「扫描类 + 解析注解元信息」,但最终都落地为 BeanDefinition。

理解这个过程,就能搞懂 Spring 容器「如何识别 Bean」「如何存储 Bean 定义」,也是理解 Spring 依赖注入、Bean 生命周期的基础。

更多推荐