【Spring源码】getBean源码实战(五)——MergedBean

getBean源码实战(五)------MergedBean

代码仓库Gitee 仓库链接

本文档的所有示例代码都可以在代码仓库中找到,建议结合代码一起阅读。

1. MergedBeanDefinition 简介

1.1 什么是 MergedBeanDefinition

MergedBeanDefinition(合并后的 Bean 定义)是 Spring 在创建 Bean 实例之前,对原始 Bean 定义进行处理后得到的完整 Bean 定义。

核心概念

  • 原始 Bean 定义 :从 XML 配置或注解中解析得到的 BeanDefinition,可能包含 parent 属性,指向父 Bean 定义
  • 合并后的 Bean 定义 :将子 Bean 定义和父 Bean 定义合并后得到的 RootBeanDefinition,包含了所有需要的信息

为什么需要合并?

在 Spring 中,Bean 定义支持继承机制。子 Bean 可以通过 parent 属性继承父 Bean 的配置,同时可以覆盖父 Bean 的属性值。例如:

xml 复制代码
<!-- 父 Bean:抽象 Bean -->
<bean id="baseService" class="com.example.abstractbean.BaseService" abstract="true">
    <property name="version" value="1.0.0"/>
    <property name="enabled" value="true"/>
</bean>

<!-- 子 Bean:继承父 Bean -->
<bean id="userService" class="com.example.abstractbean.UserService" parent="baseService">
    <property name="name" value="用户服务"/>
</bean>

在创建 userService 时,Spring 需要:

  1. 获取 userService 的原始 Bean 定义
  2. 获取 baseService 的 Bean 定义(父 Bean)
  3. 将父 Bean 的属性合并到子 Bean 中
  4. 用子 Bean 的属性覆盖父 Bean 的属性
  5. 得到最终的、完整的 Bean 定义

这个合并后的 Bean 定义就是 MergedBeanDefinition

1.2 MergedBeanDefinition 的使用场景

MergedBeanDefinition 主要在以下场景中使用:

  1. Bean 定义继承 :当子 Bean 通过 parent 属性继承父 Bean 的配置时
  2. 抽象 Bean:抽象 Bean 不能直接实例化,但可以作为父 Bean 被继承
  3. 配置复用:多个 Bean 共享相同的配置时,可以定义抽象 Bean 作为模板
  4. 属性覆盖:子 Bean 可以覆盖父 Bean 的属性值,同时继承其他属性

关键方法

  • getMergedLocalBeanDefinition(beanName):获取本地合并后的 Bean 定义
  • getMergedBeanDefinition(beanName):获取合并后的 Bean 定义(可能从父 BeanFactory 获取)
  • checkMergedBeanDefinition(mbd, beanName, args):检查合并后的 Bean 定义是否有效(如是否为抽象 Bean)

2. 示例 Bean:抽象 Bean 继承

2.1 Bean 类定义

BaseService

1:15:spring-source/code/spring-basic/src/main/java/com/example/abstractbean/BaseService.java 复制代码
package com.example.abstractbean;

/**
 * 基础服务类,用于演示抽象 bean
 */
public class BaseService {
    
    private String name;
    private String version;
    private boolean enabled;
    
    public BaseService() {
        System.out.println("BaseService 构造函数被调用");
    }
    
    // getter/setter 方法已省略,完整代码请查看源码链接
    // toString 方法已省略,完整代码请查看源码链接
    
    public void init() {
        System.out.println("BaseService 初始化方法被调用");
    }
}

UserService

1:15:spring-source/code/spring-basic/src/main/java/com/example/abstractbean/UserService.java 复制代码
package com.example.abstractbean;

/**
 * 用户服务类,继承自 BaseService
 */
public class UserService extends BaseService {
    
    private String databaseUrl;
    
    public UserService() {
        System.out.println("UserService 构造函数被调用");
    }
    
    // getter/setter 方法已省略,完整代码请查看源码链接
    // toString 方法已省略,完整代码请查看源码链接
    
    public void processUser() {
        System.out.println("UserService 处理用户数据,数据库URL: " + databaseUrl);
    }
}

OrderService

1:15:spring-source/code/spring-basic/src/main/java/com/example/abstractbean/OrderService.java 复制代码
package com.example.abstractbean;

/**
 * 订单服务类,继承自 BaseService
 */
public class OrderService extends BaseService {
    
    private String cacheUrl;
    
    public OrderService() {
        System.out.println("OrderService 构造函数被调用");
    }
    
    // getter/setter 方法已省略,完整代码请查看源码链接
    // toString 方法已省略,完整代码请查看源码链接
    
    public void processOrder() {
        System.out.println("OrderService 处理订单数据,缓存URL: " + cacheUrl);
    }
}

2.2 XML 配置

1:42:spring-source/code/spring-basic/src/main/resources/applicationContext-abstractbean.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 配置示例
        
        核心要点:
        1. 使用 abstract="true" 定义抽象 bean,抽象 bean 不会被实例化
        2. 使用 parent 属性让其他 bean 继承抽象 bean 的配置
        3. 子 bean 可以覆盖父 bean 的属性值
        4. 子 bean 可以添加自己的属性
        5. 抽象 bean 通常用于定义公共的配置,避免重复配置
    -->
    
    <!-- 定义抽象 bean:基础服务配置 -->
    <bean id="baseService" class="com.example.abstractbean.BaseService" abstract="true">
        <property name="name" value="基础服务"/>
        <property name="version" value="1.0.0"/>
        <property name="enabled" value="true"/>
    </bean>
    
    <!-- 用户服务:继承抽象 bean baseService -->
    <bean id="userService" class="com.example.abstractbean.UserService" parent="baseService">
        <!-- 覆盖父 bean 的属性 -->
        <property name="name" value="用户服务"/>
        <!-- 添加自己的属性 -->
        <property name="databaseUrl" value="jdbc:mysql://localhost:3306/user_db"/>
    </bean>
    
    <!-- 订单服务:继承抽象 bean baseService -->
    <bean id="orderService" class="com.example.abstractbean.OrderService" parent="baseService">
        <!-- 覆盖父 bean 的属性 -->
        <property name="name" value="订单服务"/>
        <!-- 添加自己的属性 -->
        <property name="cacheUrl" value="redis://localhost:6379"/>
    </bean>

</beans>

2.3 测试类

测试类位置:code/spring-basic/src/test/java/com/example/abstractbean/AbstractBeanTest.java

该测试类包含以下测试方法:

  • testAbstractBeanCannotBeInstantiated():测试抽象 bean 不能被实例化
  • testChildBeanInheritsParentProperties():测试子 bean 继承抽象 bean 的配置
  • testChildBeanOverridesParentProperties():测试子 bean 可以覆盖父 bean 的属性
  • testMultipleChildBeansInheritSameAbstractBean():测试多个子 bean 可以继承同一个抽象 bean

3. 源码分析

3.1 getMergedLocalBeanDefinition 方法

getMergedLocalBeanDefinition 方法用于获取本地(当前 BeanFactory)的合并后的 Bean 定义。

方法作用

  • mergedBeanDefinitions 缓存中获取已合并的 Bean 定义
  • 如果缓存中没有,则调用 getMergedBeanDefinition 进行合并
  • 将合并后的结果放入缓存,避免重复合并

关键点

  • 该方法只处理当前 BeanFactory 中的 Bean 定义
  • 如果 Bean 定义有 parent 属性,会递归合并父 Bean 定义
  • 合并后的 Bean 定义会被缓存,提高后续获取的性能

执行流程

java 复制代码
// AbstractBeanFactory.java
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) {
    // 1. 从缓存中获取已合并的 Bean 定义
    RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
    if (mbd != null) {
        return mbd;
    }
    
    // 2. 缓存中没有,获取原始 Bean 定义并合并
    return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}

源码说明

  • this.mergedBeanDefinitionsMap<String, RootBeanDefinition> 类型的缓存
  • getBeanDefinition(beanName)beanDefinitionMap 中获取原始 Bean 定义
  • 如果原始 Bean 定义有 parentName,会在 getMergedBeanDefinition 中递归处理

缓存机制

  • mergedBeanDefinitions 是一个 Map<String, RootBeanDefinition> 类型的缓存
  • 第一次获取某个 Bean 的合并定义时,会进行合并并放入缓存
  • 后续获取同一个 Bean 的合并定义时,直接从缓存中返回,提高性能
  • 当 Bean 定义被覆盖或重置时,会清理对应的缓存(通过 clearMergedBeanDefinition 方法)

3.2 getMergedBeanDefinition 方法

getMergedBeanDefinition 是合并 Bean 定义的核心方法。本次的重点在 getMergedBeanDefinition 这个方法中的一个分支:处理有 parentBeanName 的 Bean 定义

3.2.1 没有 parentBeanName 的情况(普通 Bean)

对于没有 parent 属性的普通 Bean(如我们在getBean源码实战(一)中分析的 UserService),处理流程相对简单:

  1. beanDefinitionMap 中获取原始 Bean 定义(GenericBeanDefinition
  2. 直接封装成 RootBeanDefinition
  3. 设置默认值(如 scope 默认为 singleton
  4. 放入 mergedBeanDefinitions 缓存
  5. 返回合并后的 Bean 定义

这种情况下,合并过程实际上只是将 GenericBeanDefinition 转换为 RootBeanDefinition,没有真正的"合并"操作。

3.2.2 处理 parentBeanName 的流程(重点)

getMergedBeanDefinition 方法的完整实现逻辑如下:

java 复制代码
// AbstractBeanFactory.java
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd) {
    return getMergedBeanDefinition(beanName, bd, null);
}

protected RootBeanDefinition getMergedBeanDefinition(
        String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd) {
    
    synchronized (this.mergedBeanDefinitions) {
        RootBeanDefinition mbd = null;
        
        // 1. 从缓存中获取已合并的 Bean 定义
        if (containingBd == null) {
            mbd = this.mergedBeanDefinitions.get(beanName);
        }
        
        if (mbd == null) {
            // 2. 检查是否有父 Bean 定义
            if (bd.getParentName() != null) {
                // 处理有 parentBeanName 的情况
                if (!beanName.equals(bd.getParentName())) {
                    // 步骤 1:解析 parentBeanName
                    String parentBeanName = transformedBeanName(bd.getParentName());
                    
                    // 步骤 2:递归获取父级 Bean 定义
                    if (parentBeanName != null && !parentBeanName.equals(beanName)) {
                        BeanDefinition pbd;
                        try {
                            // 尝试从当前 BeanFactory 获取
                            pbd = getMergedBeanDefinition(parentBeanName);
                        } catch (NoSuchBeanDefinitionException ex) {
                            // 如果当前 BeanFactory 中没有,尝试从父级 BeanFactory 获取
                            BeanFactory parentBeanFactory = getParentBeanFactory();
                            if (parentBeanFactory instanceof ConfigurableBeanFactory) {
                                pbd = ((ConfigurableBeanFactory) parentBeanFactory)
                                        .getMergedBeanDefinition(parentBeanName);
                            } else {
                                throw ex;
                            }
                        }
                        
                        // 步骤 3:创建基于父 Bean 定义的 RootBeanDefinition
                        mbd = new RootBeanDefinition(pbd);
                        // 使用子 Bean 定义的属性覆盖父 Bean 定义的属性
                        mbd.overrideFrom(bd);
                    }
                } else {
                    // parentBeanName 和 beanName 相同,说明配置错误
                    throw new BeanDefinitionStoreException(...);
                }
            } else {
                // 没有 parentBeanName,直接封装为 RootBeanDefinition
                if (bd instanceof RootBeanDefinition) {
                    mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
                } else {
                    mbd = new RootBeanDefinition(bd);
                }
            }
            
            // 步骤 4:缓存合并结果
            if (!bd.isSingleton()) {
                // 非单例 Bean 不缓存(因为每次都需要创建新实例)
                mbd.setTargetType(getResolvedType(beanName, mbd));
            } else {
                // 单例 Bean 放入缓存
                this.mergedBeanDefinitions.put(beanName, mbd);
            }
        }
        
        return mbd;
    }
}

关键步骤说明

步骤 1:解析 parentBeanName

java 复制代码
String parentBeanName = transformedBeanName(bd.getParentName());
  • 通过 transformedBeanName 方法进行解析(之前看过这个方法:去掉&,通过别名找到原始的bean)
  • 处理 FactoryBean 的前缀 & 和别名解析

步骤 2:递归获取父级 Bean 定义

java 复制代码
if (parentBeanName != null && !parentBeanName.equals(beanName)) {
    BeanDefinition pbd = getMergedBeanDefinition(parentBeanName);
    // ... 如果当前 BeanFactory 中没有,尝试从父级 BeanFactory 获取
}
  • 如果 BeanName 和 parentBeanName 不同,则通过 getMergedBeanDefinition(parentBeanName) 递归获取父级 Bean 定义
  • 该方法里也会判断是否用父级 Bean 工厂加载(getParentBeanFactory()
  • 我们当前均没有尝试使用父级,这可以先看 getMergedLocalBeanDefinition(beanName) 方法

步骤 3:合并 Bean 定义

java 复制代码
// 创建新的 RootBeanDefinition,基于父 Bean 定义
RootBeanDefinition mbd = new RootBeanDefinition(pbd);
// 使用子 Bean 定义的属性覆盖父 Bean 定义的属性
mbd.overrideFrom(bd);

overrideFrom 方法详解

overrideFromRootBeanDefinition 中的方法,用于用子 Bean 定义的属性覆盖父 Bean 定义的属性。其实现逻辑如下:

java 复制代码
// RootBeanDefinition.java
public void overrideFrom(BeanDefinition other) {
    if (other instanceof AbstractBeanDefinition) {
        AbstractBeanDefinition otherAbd = (AbstractBeanDefinition) other;
        
        // 1. 覆盖 Bean 类名和类
        if (otherAbd.hasBeanClass()) {
            setBeanClass(otherAbd.getBeanClass());
        } else if (otherAbd.getBeanClassName() != null) {
            setBeanClassName(otherAbd.getBeanClassName());
        }
        
        // 2. 覆盖作用域
        if (otherAbd.hasPropertyValues()) {
            setScope(otherAbd.getScope());
        }
        
        // 3. 覆盖工厂相关属性
        if (otherAbd.getFactoryBeanName() != null) {
            setFactoryBeanName(otherAbd.getFactoryBeanName());
        }
        if (otherAbd.getFactoryMethodName() != null) {
            setFactoryMethodName(otherAbd.getFactoryMethodName());
        }
        
        // 4. 覆盖构造参数
        if (otherAbd.hasConstructorArgumentValues()) {
            getConstructorArgumentValues().addArgumentValues(
                    otherAbd.getConstructorArgumentValues());
        }
        
        // 5. 覆盖属性值(PropertyValues)
        if (otherAbd.hasPropertyValues()) {
            MutablePropertyValues propertyValues = new MutablePropertyValues();
            propertyValues.addPropertyValues(getPropertyValues());
            propertyValues.addPropertyValues(otherAbd.getPropertyValues());
            setPropertyValues(propertyValues);
        }
        
        // 6. 覆盖方法覆盖(lookup-method 和 replaced-method)
        if (otherAbd.hasMethodOverrides()) {
            getMethodOverrides().addOverrides(otherAbd.getMethodOverrides());
        }
        
        // 7. 覆盖其他属性
        setLazyInit(otherAbd.isLazyInit());
        setAutowireMode(otherAbd.getAutowireMode());
        setDependencyCheck(otherAbd.getDependencyCheck());
        if (otherAbd.getDependsOn() != null) {
            setDependsOn(otherAbd.getDependsOn());
        }
        setAutowireCandidate(otherAbd.isAutowireCandidate());
        setPrimary(otherAbd.isPrimary());
        setRole(otherAbd.getRole());
        setDescription(otherAbd.getDescription());
        setResourceDescription(otherAbd.getResourceDescription());
        setSource(otherAbd.getSource());
        
        // 8. 覆盖元数据属性
        copyAttributesFrom(otherAbd);
    }
}

覆盖的属性列表

  • setBeanClassName:Bean 类名
  • setBeanClass:Bean 类
  • setScope:作用域
  • setFactoryBeanName:工厂 Bean 名称
  • setFactoryMethodName:工厂方法名称
  • 构造参数:构造函数参数值(通过 addArgumentValues 添加)
  • properties 值:属性值(通过 addPropertyValues 添加,子 Bean 的属性会覆盖父 Bean 的同名属性)
  • 覆盖方法:lookup-method 和 replaced-method(通过 addOverrides 添加)
  • setLazyInit:懒加载
  • setAutowireMode:自动装配模式
  • setDependencyCheck:依赖检查
  • setDependsOn:依赖的 Bean
  • setAutowireCandidate:是否作为自动装配候选
  • setPrimary:是否为主要 Bean
  • setRole:Bean 的角色
  • setDescription:描述信息
  • setResourceDescription:资源描述
  • setSource:源对象
  • 元数据属性:通过 copyAttributesFrom 复制

步骤 4:缓存合并结果

java 复制代码
// 单例 Bean 放入缓存
if (bd.isSingleton()) {
    this.mergedBeanDefinitions.put(beanName, mbd);
} else {
    // 非单例 Bean 不缓存(因为每次都需要创建新实例)
    mbd.setTargetType(getResolvedType(beanName, mbd));
}
  • 将 beanName 与合并后的 bean 定义放入 mergedBeanDefinitions 缓存中
  • 注意:只有单例 Bean 才会被缓存,非单例 Bean(prototype)每次获取时都会重新合并
  • 避免重复合并,提高性能

步骤 5:复制相关缓存

java 复制代码
// AbstractBeanFactory.java
protected void copyRelevantMergedBeanDefinitionCaches(
        RootBeanDefinition source, RootBeanDefinition target) {
    
    // 复制方法覆盖缓存
    if (source.hasMethodOverrides()) {
        target.copyMethodOverridesFrom(source);
    }
    
    // 复制其他相关缓存信息
    // ...
}
  • 这个方法的作用暂时保留,个人理解是需要重新覆盖 bean 时才会走进来
  • 主要用于处理 Bean 定义覆盖的场景
  • 将父 Bean 定义中的一些缓存信息(如方法覆盖缓存)复制到子 Bean 定义中
  • 确保子 Bean 定义能够正确继承父 Bean 定义的方法覆盖信息

完整流程对比

场景 处理方式 结果
没有 parentBeanName 直接封装为 RootBeanDefinition 原始 Bean 定义的完整副本
有 parentBeanName 递归合并父 Bean 定义,然后覆盖 父 Bean + 子 Bean 的合并结果
3.2.3 为什么需要合并而不是直接使用原始 Bean 定义?

💡 思考问题:其实在 bean 定义读取时也有一个 userService 的 bean 定义,那这里为什么不直接使用?

原因分析

  1. 原始 Bean 定义不完整

    • 原始 Bean 定义(GenericBeanDefinition)只包含子 Bean 自己的配置
    • 例如 userService 的原始定义中只有 name="用户服务"databaseUrl
    • 缺少从父 Bean baseService 继承的属性(versionenabled
  2. 需要合并父 Bean 的属性

    • 父 Bean baseService 定义了 version="1.0.0"enabled="true"
    • 这些属性需要合并到子 Bean 中,才能得到完整的 Bean 定义
  3. 支持属性覆盖

    • 子 Bean 可以覆盖父 Bean 的属性(如 name 属性)
    • 合并过程会正确处理覆盖逻辑
  4. 统一 Bean 定义类型

    • 合并后的 Bean 定义统一为 RootBeanDefinition 类型
    • 便于后续的 Bean 创建流程使用

示例说明

xml 复制代码
<!-- 父 Bean -->
<bean id="baseService" abstract="true">
    <property name="version" value="1.0.0"/>
    <property name="enabled" value="true"/>
    <property name="name" value="基础服务"/>
</bean>

<!-- 子 Bean -->
<bean id="userService" parent="baseService">
    <property name="name" value="用户服务"/>  <!-- 覆盖父 Bean 的 name -->
    <property name="databaseUrl" value="jdbc:mysql://..."/>
</bean>

原始 Bean 定义userService):

  • name = "用户服务"
  • databaseUrl = "jdbc:mysql://..."
  • ❌ 缺少 versionenabled

合并后的 Bean 定义userService):

  • name = "用户服务"(子 Bean 覆盖)
  • version = "1.0.0"(从父 Bean 继承)
  • enabled = true(从父 Bean 继承)
  • databaseUrl = "jdbc:mysql://..."(子 Bean 自己的属性)
  • ✅ 包含所有需要的属性

3.3 checkMergedBeanDefinition 方法

得到了融合后的 bean 定义后,会通过 this.checkMergedBeanDefinition(mbd, beanName, args) 方法检查 bean 定义是否有效。

方法作用

  • 检查合并后的 Bean 定义是否为抽象 Bean
  • 抽象 Bean 不能实例化,如果尝试获取抽象 Bean,会抛出 BeanIsAbstractException 异常

检查逻辑

java 复制代码
// AbstractBeanFactory.java
protected void checkMergedBeanDefinition(RootBeanDefinition mbd, String beanName, @Nullable Object[] args)
        throws BeanDefinitionStoreException {
    
    // 检查是否为抽象 Bean
    if (mbd.isAbstract()) {
        throw new BeanIsAbstractException(beanName);
    }
}

源码位置AbstractBeanFactory.checkMergedBeanDefinition()

调用时机 :在 doGetBean 方法中,获取合并后的 Bean 定义后立即调用:

java 复制代码
// AbstractBeanFactory.java - doGetBean 方法片段
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType,
        @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
    
    // ... 前面的步骤(翻译 beanName、获取单例等)
    
    // 获取合并后的 Bean 定义
    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    
    // 检查 Bean 定义是否有效(是否为抽象 Bean)
    checkMergedBeanDefinition(mbd, beanName, args);
    
    // ... 后续的 Bean 创建流程
}

为什么需要这个检查?

  1. 抽象 Bean 的定义:抽象 Bean 通常作为模板,用于被其他 Bean 继承
  2. 防止误用:如果直接尝试获取抽象 Bean,说明配置或使用有误
  3. 提前发现错误:在 Bean 创建之前就发现配置问题,避免浪费资源

示例

java 复制代码
// 尝试获取抽象 Bean baseService,会抛出异常
BeanFactory factory = new XmlBeanFactory(
    new ClassPathResource("applicationContext-abstractbean.xml")
);

// 这会抛出 BeanIsAbstractException
factory.getBean("baseService", BaseService.class);

正确的使用方式

java 复制代码
// 获取继承抽象 Bean 的子 Bean,这是正确的
UserService userService = factory.getBean("userService", UserService.class);
// userService 包含了从 baseService 继承的所有属性

3.4 后续流程

其实到这里后续的逻辑都基本一致了,因为这 UserService 的 bean 定义都完全了,剩下的就是实例化后,进行的赋值和依赖赋值的步骤了。

4. 调试过程

4.1 调试 userService Bean 的合并过程

userService Bean 为例,调试其 Bean 定义的合并过程:

步骤 1:获取 userService 的原始 Bean 定义

doGetBean 方法中,调用 getMergedLocalBeanDefinition("userService")

  • beanDefinitionMap 中获取 userService 的原始 Bean 定义(GenericBeanDefinition
  • 发现该 Bean 定义有 parentName = "baseService"

步骤 2:合并父 Bean 定义

调用 getMergedBeanDefinition("userService")

  • 检测到 parentBeanName = "baseService"
  • 通过 transformedBeanName 解析 parentBeanName(本例中无需处理)
  • 递归调用 getMergedBeanDefinition("baseService") 获取父 Bean 定义
  • 父 Bean baseService 没有父 Bean,直接返回其 RootBeanDefinition

步骤 3:创建合并后的 Bean 定义

  • 创建新的 RootBeanDefinition,基于父 Bean baseService 的定义
  • 调用 overrideFrom(userService的原始定义) 进行属性覆盖
  • 最终得到包含所有属性的完整 Bean 定义

步骤 4:检查 Bean 定义

调用 checkMergedBeanDefinition

  • 检查合并后的 Bean 定义是否为抽象
  • userService 不是抽象 Bean,检查通过

步骤 5:后续流程

  • Bean 定义合并完成,后续进入正常的 Bean 创建流程
  • 实例化、属性赋值、依赖注入等步骤与普通 Bean 一致

4.2 调试抽象 Bean 的检查

尝试获取抽象 Bean baseService

java 复制代码
factory.getBean("baseService", BaseService.class);

执行流程

  1. 调用 getMergedBeanDefinition("baseService")
  2. 合并 Bean 定义(baseService 没有父 Bean,直接返回)
  3. 调用 checkMergedBeanDefinition
  4. 检测到 baseService 是抽象 Bean(abstract="true"
  5. 抛出 BeanIsAbstractException 异常

5. 总结

5.1 MergedBeanDefinition 的核心作用

  1. Bean 定义继承 :支持子 Bean 通过 parent 属性继承父 Bean 的配置
  2. 属性合并:将父 Bean 和子 Bean 的属性合并,得到完整的 Bean 定义
  3. 属性覆盖:子 Bean 可以覆盖父 Bean 的属性值
  4. 配置复用:通过抽象 Bean 实现配置的复用,提高可维护性

5.2 关键方法总结

方法 作用 说明
getMergedLocalBeanDefinition 获取本地合并后的 Bean 定义 从缓存获取或调用 getMergedBeanDefinition
getMergedBeanDefinition 合并 Bean 定义 处理 parentBeanName,递归合并父 Bean 定义
overrideFrom 覆盖属性 用子 Bean 定义的属性覆盖父 Bean 定义的属性
checkMergedBeanDefinition 检查 Bean 定义 检查是否为抽象 Bean,防止实例化抽象 Bean

5.3 合并流程总结

复制代码
原始 Bean 定义(GenericBeanDefinition)
    ↓
检测 parentBeanName
    ↓
递归获取父 Bean 定义
    ↓
创建 RootBeanDefinition(基于父 Bean)
    ↓
调用 overrideFrom(用子 Bean 属性覆盖)
    ↓
合并后的 Bean 定义(RootBeanDefinition)
    ↓
检查是否为抽象 Bean
    ↓
后续 Bean 创建流程

5.4 与之前文章的联系

  • Bean 定义加载 :在 XML 解析时,parent 属性被解析为 parentName 存储在 GenericBeanDefinition
  • Bean 定义注册 :原始 Bean 定义被注册到 beanDefinitionMap
  • Bean 创建 :在 doGetBean 方法中,通过 getMergedLocalBeanDefinition 获取合并后的 Bean 定义
  • 后续流程:合并后的 Bean 定义用于 Bean 的实例化、属性赋值和依赖注入

5.5 学习要点

  1. 理解 Bean 定义继承机制:Spring 通过合并机制实现 Bean 定义的继承
  2. 掌握合并流程 :理解 getMergedBeanDefinition 的递归合并逻辑
  3. 理解属性覆盖:子 Bean 的属性会覆盖父 Bean 的同名属性
  4. 理解抽象 Bean:抽象 Bean 不能实例化,只能作为父 Bean 被继承
  5. 理解缓存机制:合并后的 Bean 定义会被缓存,避免重复合并

通过本文的分析,我们深入理解了 Spring 如何处理 Bean 定义的继承和合并,这是 Spring IoC 容器中一个重要的机制,为配置复用和代码简化提供了强大的支持。

相关推荐
trayvontang3 分钟前
Spring Gateway核心概念、流程及原理
spring·gateway·spring gateway
郝学胜-神的一滴4 分钟前
线程同步:并行世界的秩序守护者
java·linux·开发语言·c++·程序人生
superman超哥5 分钟前
Rust 移动语义(Move Semantics)的工作原理:零成本所有权转移的深度解析
开发语言·后端·rust·工作原理·深度解析·rust移动语义·move semantics
superman超哥16 分钟前
Rust 所有权转移在函数调用中的表现:编译期保证的零成本抽象
开发语言·后端·rust·函数调用·零成本抽象·rust所有权转移
源代码•宸19 分钟前
goframe框架签到系统项目开发(实现总积分和积分明细接口、补签日期校验)
后端·golang·postman·web·dao·goframe·补签
无限进步_24 分钟前
【C语言】堆(Heap)的数据结构与实现:从构建到应用
c语言·数据结构·c++·后端·其他·算法·visual studio
掉鱼的猫25 分钟前
灵动如画 —— 初识 Solon Graph Fluent API 编排
java·openai·workflow
初次攀爬者25 分钟前
基于知识库的知策智能体
后端·ai编程
喵叔哟25 分钟前
16.项目架构设计
后端·docker·容器·.net