当前位置: 首页 > article >正文

从零手写Spring IoC容器(一):传统对象管理的困境与基础容器的实现

从零手写Spring IoC容器(一):传统对象管理的困境与基础容器的实现

一、传统开发方式的问题

1.1 手动创建对象的痛点

在传统开发中,我们经常看到这样的代码:

public class OrderService {
    // 直接在代码中创建依赖对象
    private UserService userService = new UserService();
    private PaymentService paymentService = new PaymentService();
    private LogisticsService logisticsService = new LogisticsService();
    private NotificationService notificationService = new EmailNotificationService();
    
    public Order createOrder(String userId, List<Product> products) {
      //业务方法
    }
}

这种方式的三个主要问题:

  1. 紧耦合:对象创建代码散落在各处
  2. 难以管理:依赖关系复杂时容易出错
  3. 缺乏统一控制:无法实现统一的扩展点

1.2 IoC容器的解决方案

Spring框架通过BeanFactory接口实现控制反转:

  • 将对象的创建、装配过程交给容器
  • 通过统一接口获取对象
  • 支持多种配置方式(XML/注解)

二、基础容器实现

2.1 设计思路

在这里插入图片描述

2.2 核心接口设计

在这里插入图片描述

2.3定义核心接口

/**
 * bean工厂顶级接口
 * 用于提供基础的获取bean的功能
 * @author 方
 */
public interface BeanFactory {
    /**
     * 根据bean名称获取bean实例
     *
     * @param name bean名称
     * @return bean实例
     * @throws BeansException 如果无法获取bean
     */
    Object getBean(String name) throws BeansException;

    /**
     * 根据bean名称和类型获取bean实例
     *
     * @param name bean名称
     * @param requiredType 需要的bean类型
     * @return bean实例
     * @throws BeansException 如果无法获取bean
     */
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    /**
     * 根据类型获取bean实例
     *
     * @param requiredType 需要的bean类型
     * @return bean实例
     * @throws BeansException 如果无法获取bean
     */
    <T> T getBean(Class<T> requiredType) throws BeansException;

    /**
     * 判断是否包含指定名称的bean
     *
     * @param name bean名称
     * @return 如果包含返回true,否则返回false
     */
    boolean containsBean(String name);

    /**
     * 判断指定名称的bean是否是单例
     *
     * @param name bean名称
     * @return 如果是单例返回true,否则返回false
     * @throws BeansException 如果无法判断bean的作用域
     */
    boolean isSingleton(String name) throws BeansException;

    /**
     * 判断指定名称的bean是否是原型
     *
     * @param name bean名称
     * @return 如果是原型返回true,否则返回false
     * @throws BeansException 如果无法判断bean的作用域
     */
    boolean isPrototype(String name) throws BeansException;

    /**
     * 获取指定名称bean的类型
     *
     * @param name bean名称
     * @return bean的类型,如果不存在返回null
     * @throws BeansException 如果无法获取bean的类型
     */
    Class<?> getType(String name) throws BeansException;

}

2.4最简单的容器实现

public class SimpleBeanFactory implements BeanFactory {
    private Map<String, Object> beanMap = new ConcurrentHashMap<>();

    /**
     * 注册bean
     * @param name
     * @param bean
     */
    public void registerBean(String name, Object bean) {
        beanMap.put(name, bean);
    }

    /**
     * 根据bean名称获取bean
     *
     * @param name bean名称
     * @return bean对象
     */
    @Override
    public Object getBean(String name) {
        if (containsBean(name)){
            return beanMap.get(name);
        }
        throw new RuntimeException("no bean named " + name);
    }

    /**
     * 根据bean名称和类型获取bean数据
     *
     * @param name         bean名称
     * @param requiredType bean的类型
     * @return bean
     */
    @Override
    public <T> T getBean(String name, Class<T> requiredType) {
        Object bean = getBean(name);
        // 检查类型
        if (requiredType.isInstance(bean)){
            // 如果类型符合就转成这个类型返回出去
            return requiredType.cast(bean);
        }
        return null;
    }

    /**
     * 判断是否包含bean
     *
     * @param name bean名称
     * @return boolean
     */
    @Override
    public boolean containsBean(String name) {
        return beanMap.containsKey(name);
    }

}

容器特点:

  • 使用ConcurrentHashMap保证线程安全
  • 提供简单的注册机制
  • 按需抛出运行时异常

三、代码验证与使用示例

public class Test1 {
    public static void main(String[] args) {
        // 初始化容器
        SimpleBeanFactory beanFactory = new SimpleBeanFactory();
        
        // 注册Bean
        UserService userService = new UserService();
        beanFactory.registerBean("userService", userService);
        
        // 获取Bean
        UserService service = beanFactory.getBean("userService", UserService.class);
        System.out.println("获取Bean成功: " + service);
    }
}

四、不足之处与 Spring 的对比

虽然当前的 SimpleBeanFactory 已经实现了基本的 IoC 容器功能,能够手动注册和获取 Bean,但它仍然存在诸多局限性,与 Spring IoC 容器相比还有很大的提升空间。以下是几个关键问题:

4.1 属性依赖注入问题

目前 SimpleBeanFactory 只能通过 registerBean 方法手动注册对象,并且对象的依赖关系需要手动构造。例如:

UserRepository userRepository = new UserRepository();
UserService userService = new UserService(userRepository);
beanFactory.registerBean("userRepository", userRepository);
beanFactory.registerBean("userService", userService);

这种方式的问题:

  • 需要手动实例化 UserRepository 并传递给 UserService,并未真正实现依赖注入(Dependency Injection)。
  • UserService 依赖多个组件时,构造过程会变得复杂。
  • 如果 UserService 依赖于 OrderService,而 OrderService 又依赖 UserRepository,维护起来会越来越困难。

改进方向:
在后续章节中,我们可以引入 构造器注入Setter 注入,让 IoC 容器自动完成依赖注入,减少手动管理 Bean 依赖的复杂度。

4.2 Bean 的定义与注册

当前 SimpleBeanFactory 直接存储了 Object 类型的实例:

private Map<String, Object> beanMap = new ConcurrentHashMap<>();

但在实际应用中,我们通常需要提前 定义 Bean,而不是直接存储实例。Spring 采用 BeanDefinition 来描述 Bean,包括:

  • Bean 的 class 类型
  • Bean 的 作用域(singleton/prototype)
  • Bean 的 依赖关系
  • Bean 的 初始化方法与销毁方法

改进方向:
在下一步实现中,我们需要设计 BeanDefinition 类,并提供 基于 XML 或注解的 Bean 配置加载机制,从而支持更灵活的 Bean 注册方式。

4.3 Bean 的生命周期管理

目前 SimpleBeanFactory 只是简单地存储和返回对象,没有涉及 Bean 的完整生命周期管理。Spring 的 Bean 具有完整的生命周期,包括:

  1. 实例化(Instantiation):创建对象
  2. 属性填充(Dependency Injection):注入依赖
  3. 初始化(Initialization):调用 @PostConstruct 方法或 init-method
  4. 使用(In Use):对象被应用层使用
  5. 销毁(Destruction):调用 @PreDestroy 方法或 destroy-method

当前缺陷:

  • 没有提供生命周期回调方法,例如 Bean 初始化和销毁时执行的逻辑。
  • 缺少 BeanPostProcessor 机制,无法在 Bean 实例化前后进行增强,如 Spring AOP 代理的创建。

改进方向:

  • 设计 BeanPostProcessor 机制,支持在 Bean 实例化前后进行自定义处理。
  • 提供 init-methoddestroy-method 支持,管理 Bean 生命周期。

4.4 Bean 作用域管理

在 Spring IoC 容器中,Bean 作用域(Scope)是一个重要概念,主要包括:

  • 单例模式(Singleton):容器只创建一个实例,所有请求返回同一对象(默认)。
  • 原型模式(Prototype):每次获取 Bean 时,都会创建新的对象实例。

当前 SimpleBeanFactory 仅支持手动注册的对象,且默认认为所有 Bean 都是 单例,但 isSingleton 方法尚未实现:

五.引出第二章:实现 Bean 的定义与注册

当前的 SimpleBeanFactory 只能通过手动注册对象的方式管理 Bean,但在实际应用中,Bean 的管理需要更加灵活和可扩展。

在第二章中,我们将引入 BeanDefinition 概念,用于描述 Bean 的元信息,使容器可以根据定义来创建和管理 Bean。接下来的关键点包括:

  1. BeanDefinition 的设计
    • Bean 的名称、类型、作用域(单例/原型)等元信息
    • 构造参数和属性依赖的描述
  2. Bean 的注册机制
    • 通过 BeanDefinition 统一管理 Bean 的定义
    • 设计 BeanDefinitionRegistry 统一注册 Bean
  3. 实例化策略(初步实现)
    • 目前 SimpleBeanFactory 直接存储实例,后续需要基于 BeanDefinition 动态创建对象
    • 通过反射机制创建 Bean(暂不涉及 XML 或注解解析)

关键点包括:

  1. BeanDefinition 的设计
    • Bean 的名称、类型、作用域(单例/原型)等元信息
    • 构造参数和属性依赖的描述
  2. Bean 的注册机制
    • 通过 BeanDefinition 统一管理 Bean 的定义
    • 设计 BeanDefinitionRegistry 统一注册 Bean
  3. 实例化策略(初步实现)
    • 目前 SimpleBeanFactory 直接存储实例,后续需要基于 BeanDefinition 动态创建对象
    • 通过反射机制创建 Bean(暂不涉及 XML 或注解解析)

第二章将会完善 IoC 容器的 Bean 注册与定义机制,为后续的依赖注入、作用域管理和生命周期控制奠定基础。


http://www.kler.cn/a/535350.html

相关文章:

  • 免费windows pdf编辑工具
  • leetCode刷题-图、回溯相关
  • NacosRce到docker逃逸实战
  • mysql8安装时提示-缺少Microsoft Visual C++ 2019 x64 redistributable
  • 在线教程丨YOLO系列10年更新11个版本,最新模型在目标检测多项任务中达SOTA
  • string类OJ练习题
  • 参数3说明
  • Windows 中学习Docker环境准备3、在Ubuntu中安装Docker
  • Linux之守护进程,应用层协议
  • 【数据结构】_队列的结构与实现
  • 【Leetcode 每日一题】90. 子集 II
  • 基于多重算法的医院增强型50G全光网络设计与实践:构建智慧医疗新基石(上)
  • MS SQL Server partition by 函数实战二 编排考场人员
  • vite共享配置之---css相关
  • 【玩转 Postman 接口测试与开发2_018】第14章:利用 Postman 初探 API 安全测试
  • MAC OS安装Homebrew
  • android用eclipse开发碰到65535问题的完美解决方案
  • DMZ区的作用和原则
  • 【Windows 开发NVIDIA相关组件】CUDA、cuDNN、TensorRT
  • 3.5 Go(特殊函数)
  • 计算机毕业设计hadoop+spark+hive民宿推荐系统 酒店推荐系统 民宿价格预测 酒店价预测 机器学习 深度学习 Python爬虫 HDFS集群
  • 嵌入式硬件篇---OpenMV基本使用自动增益\曝光\白平衡
  • Unity VideoPlayer播放视屏不清晰的一种情况
  • 网络安全风险量化值 网络安全风险控制
  • C# OpenCV机器视觉:利用TrashNet实现垃圾分类
  • Google地图瓦片爬虫——进阶版