2025年11月23日

[手写 MiniSpring 框架教程] 第 03 讲:Bean 定义与注册

📖 本讲目标

  • 完善 BeanDefinition 的设计
  • 实现 Bean 名称生成策略
  • 理解 Bean 注册流程
  • 实现异常处理机制

🎯 BeanDefinition 完整设计

设计思路

BeanDefinition 是容器管理 Bean 的核心数据结构,它需要存储:

  1. 基本信息:类类型、Bean 名称
  2. 作用域信息:singleton 或 prototype
  3. 生命周期信息:懒加载、初始化方法、销毁方法
  4. 依赖信息:依赖的其他 Bean

完整实现

更新 src/main/java/com/minispring/beans/factory/config/BeanDefinition.java

package com.minispring.beans.factory.config;

import java.util.HashSet;
import java.util.Set;

/**
 * BeanDefinition 类
 * 存储 Bean 的元数据信息,是 IoC 容器管理 Bean 的核心数据结构
 */
public class BeanDefinition {

    // Bean 的 Class 类型,用于反射创建实例
    private Class<?> beanClass;

    // Bean 在容器中的唯一名称
    private String beanName;

    // Bean 的作用域:singleton(单例)或 prototype(原型)
    private String scope = "singleton"; // 默认单例

    // 是否懒加载
    private boolean lazy = false;

    // 是否为主要候选者(当有多个相同类型的 Bean 时优先选择)
    private boolean primary = false;

    // 依赖的其他 Bean 名称集合
    private Set<String> dependsOn = new HashSet<>();

    // 初始化方法名称
    private String initMethodName;

    // 销毁方法名称
    private String destroyMethodName;

    /**
     * 构造函数
     */
    public BeanDefinition() {
    }

    /**
     * 构造函数
     * @param beanClass Bean 的 Class 类型
     */
    public BeanDefinition(Class<?> beanClass) {
        this.beanClass = beanClass;
        this.beanName = generateDefaultBeanName(beanClass);
    }

    /**
     * 完整构造函数
     * @param beanClass Bean 的 Class 类型
     * @param beanName Bean 名称
     */
    public BeanDefinition(Class<?> beanClass, String beanName) {
        this.beanClass = beanClass;
        this.beanName = beanName;
    }

    /**
     * 生成默认的 Bean 名称(类名首字母小写)
     * @param clazz Class 类型
     * @return Bean 名称
     */
    private String generateDefaultBeanName(Class<?> clazz) {
        String simpleName = clazz.getSimpleName();
        return Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);
    }

    // ==================== Getter 和 Setter 方法 ====================

    public Class<?> getBeanClass() {
        return beanClass;
    }

    public void setBeanClass(Class<?> beanClass) {
        this.beanClass = beanClass;
    }

    public String getBeanName() {
        return beanName;
    }

    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    /**
     * 判断是否为单例模式
     * @return true 表示单例
     */
    public boolean isSingleton() {
        return "singleton".equals(scope);
    }

    /**
     * 判断是否为原型模式
     * @return true 表示原型
     */
    public boolean isPrototype() {
        return "prototype".equals(scope);
    }

    public boolean isLazy() {
        return lazy;
    }

    public void setLazy(boolean lazy) {
        this.lazy = lazy;
    }

    public boolean isPrimary() {
        return primary;
    }

    public void setPrimary(boolean primary) {
        this.primary = primary;
    }

    public Set<String> getDependsOn() {
        return dependsOn;
    }

    public void setDependsOn(Set<String> dependsOn) {
        this.dependsOn = dependsOn;
    }

    /**
     * 添加依赖的 Bean 名称
     * @param beanName 依赖的 Bean 名称
     */
    public void addDependsOn(String beanName) {
        this.dependsOn.add(beanName);
    }

    public String getInitMethodName() {
        return initMethodName;
    }

    public void setInitMethodName(String initMethodName) {
        this.initMethodName = initMethodName;
    }

    public String getDestroyMethodName() {
        return destroyMethodName;
    }

    public void setDestroyMethodName(String destroyMethodName) {
        this.destroyMethodName = destroyMethodName;
    }

    @Override
    public String toString() {
        return "BeanDefinition{" +
                "beanClass=" + beanClass.getName() +
                ", beanName='" + beanName + ''' +
                ", scope='" + scope + ''' +
                ", lazy=" + lazy +
                ", primary=" + primary +
                '}';
    }
}

🏷️ Bean 名称生成策略

默认命名规则

Spring 框架的默认 Bean 命名规则:

  • 如果类名以大写字母开头,将首字母转为小写
  • 例如:UserServiceuserService
  • 例如:OrderRepositoryorderRepository

实现细节

private String generateDefaultBeanName(Class<?> clazz) {
    String simpleName = clazz.getSimpleName();
    if (simpleName == null || simpleName.isEmpty()) {
        throw new IllegalArgumentException("类名不能为空");
    }

    // 首字母转小写
    char firstChar = simpleName.charAt(0);
    if (Character.isLowerCase(firstChar)) {
        return simpleName; // 已经是小写开头
    }

    return Character.toLowerCase(firstChar) + simpleName.substring(1);
}

特殊情况处理

  1. 连续大写字母XMLParserXMLParser(保持原样)
  2. 单个字母Aa
  3. 空类名:抛出异常

⚠️ 异常处理机制

异常类设计

创建异常类层次结构:

1. BeansException(基础异常)

package com.minispring.beans.factory;

/**
 * Bean 操作相关的通用异常基类
 */
public class BeansException extends RuntimeException {
    public BeansException(String message) {
        super(message);
    }

    public BeansException(String message, Throwable cause) {
        super(message, cause);
    }
}

2. NoSuchBeanDefinitionException

package com.minispring.beans.factory;

/**
 * 当容器中找不到指定名称或类型的 Bean 时抛出此异常
 */
public class NoSuchBeanDefinitionException extends BeansException {
    public NoSuchBeanDefinitionException(String beanName) {
        super("找不到 Bean: " + beanName);
    }

    public NoSuchBeanDefinitionException(Class<?> beanType) {
        super("找不到类型为 " + beanType.getName() + " 的 Bean");
    }
}

3. BeanNotOfRequiredTypeException

package com.minispring.beans.factory;

/**
 * 当 Bean 的类型与期望的类型不匹配时抛出此异常
 */
public class BeanNotOfRequiredTypeException extends BeansException {
    private final String beanName;
    private final Class<?> requiredType;
    private final Class<?> actualType;

    public BeanNotOfRequiredTypeException(String beanName, Class<?> requiredType, Class<?> actualType) {
        super("Bean '" + beanName + "' 的类型不匹配: 期望类型 " + requiredType.getName() +
                ", 实际类型 " + actualType.getName());
        this.beanName = beanName;
        this.requiredType = requiredType;
        this.actualType = actualType;
    }

    // Getters...
}

4. NoUniqueBeanDefinitionException

package com.minispring.beans.factory;

/**
 * 当容器中存在多个相同类型的 Bean 且无法确定使用哪一个时抛出此异常
 */
public class NoUniqueBeanDefinitionException extends BeansException {
    private final Class<?> beanType;
    private final int foundBeansCount;

    public NoUniqueBeanDefinitionException(Class<?> beanType, int foundBeansCount) {
        super("找到 " + foundBeansCount + " 个类型为 " + beanType.getName() + 
              " 的 Bean,无法确定使用哪一个");
        this.beanType = beanType;
        this.foundBeansCount = foundBeansCount;
    }

    // Getters...
}

更新 ApplicationContext 使用异常

@Override
public Object getBean(String beanName) throws Exception {
    BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
    if (beanDefinition == null) {
        throw new NoSuchBeanDefinitionException(beanName);
    }
    // ...
}

@Override
public <T> T getBean(String beanName, Class<T> requiredType) throws Exception {
    Object bean = getBean(beanName);
    if (requiredType != null && !requiredType.isInstance(bean)) {
        throw new BeanNotOfRequiredTypeException(beanName, requiredType, bean.getClass());
    }
    return (T) bean;
}

🔄 Bean 注册流程

注册方法

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
    // 1. 参数校验
    if (beanName == null || beanName.isEmpty()) {
        throw new IllegalArgumentException("Bean 名称不能为空");
    }
    if (beanDefinition == null) {
        throw new IllegalArgumentException("BeanDefinition 不能为空");
    }

    // 2. 检查是否已存在
    if (beanDefinitionMap.containsKey(beanName)) {
        throw new RuntimeException("Bean 名称重复: " + beanName);
    }

    // 3. 注册
    beanDefinitionMap.put(beanName, beanDefinition);

    System.out.println("注册 Bean: " + beanName + " [scope=" + 
                      beanDefinition.getScope() + "]");
}

注册时机

Bean 的注册可以在以下时机进行:

  1. 手动注册:通过 registerBeanDefinition 方法
  2. 自动扫描:通过组件扫描器(后续实现)
  3. 配置类:通过 @Configuration 类(不在本教程范围)

🧪 测试示例

package com.minispring.demo;

import com.minispring.beans.factory.config.BeanDefinition;
import com.minispring.beans.factory.NoSuchBeanDefinitionException;
import com.minispring.context.ApplicationContext;

public class BeanDefinitionTest {
    public static void main(String[] args) throws Exception {
        ApplicationContext context = new ApplicationContext();

        // 创建 BeanDefinition
        BeanDefinition bd = new BeanDefinition();
        bd.setBeanClass(String.class);
        bd.setBeanName("testBean");
        bd.setScope("singleton");
        bd.setLazy(false);

        // 注册
        context.registerBeanDefinition("testBean", bd);

        // 测试获取
        Object bean = context.getBean("testBean");
        System.out.println("Bean: " + bean);

        // 测试异常
        try {
            context.getBean("nonExistentBean");
        } catch (NoSuchBeanDefinitionException e) {
            System.out.println("正确捕获异常: " + e.getMessage());
        }
    }
}

📝 本讲总结

本讲我们完成了:

  1. ✅ 完善了 BeanDefinition 的完整设计
  2. ✅ 实现了 Bean 名称生成策略
  3. ✅ 创建了异常处理机制
  4. ✅ 实现了 Bean 注册流程
  5. ✅ 添加了 Bean 的元数据属性(lazy、primary、dependsOn 等)

🎯 关键点回顾

  1. BeanDefinition:存储 Bean 的所有元数据信息
  2. 命名策略:首字母小写的默认命名规则
  3. 异常体系:完善的异常类层次结构
  4. 注册机制:通过 Map 存储 BeanDefinition

🚀 下一讲预告

在下一讲中,我们将实现 依赖注入 – 字段注入,包括:

  • @Autowired 注解实现
  • 反射机制应用
  • 按类型自动匹配
  • 循环依赖检测

准备好了吗?让我们继续 第 04 讲:依赖注入 – 字段注入


思考题

  1. BeanDefinition 为什么要存储这么多信息?
  2. 为什么使用 Set 存储 dependsOn?
  3. 异常类为什么要设计成层次结构?

“以书为舟,遨游尘世”,
最好的免费 kindle 电子书分享站:

You may also like...

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注


*