【开源项目】手写迷你版 Spring IoC 容器,帮你彻底理解 Spring 核心原理!🚀

📖 前言

你是否曾经好奇:

  • Spring 的 IoC 容器到底是怎么工作的?
  • @Autowired 是如何实现自动注入的?
  • Bean 的生命周期管理有什么奥秘?

阅读源码太复杂?理论讲解太抽象?

别担心!我为你准备了一个超轻量级的 Spring 实现 —— Mini-Spring,用最简洁的代码(仅几百行),带你从零理解 Spring 的核心机制!


🎯 项目介绍

Mini-Spring 是一个简化版的 Spring IoC 容器实现,专注于帮助开发者理解 Spring 最核心的两个概念:

  1. IoC(控制反转) - 容器管理对象的生命周期
  2. DI(依赖注入) - 自动装配 Bean 之间的依赖关系

✨ 核心特性

自动扫描 - 基于 @Component 注解自动发现并注册组件
依赖注入 - 支持 @Autowired 字段级别的自动注入
生命周期管理 - 单例/原型模式,通过 @Scope 灵活配置
零依赖 - 纯 Java 实现,无需任何第三方库
代码精简 - 核心代码不到 500 行,通俗易懂


💡 为什么需要这个项目?

痛点分析

学习方式 问题
直接读 Spring 源码 代码量巨大,设计复杂,新手难以入门
看理论文章 抽象难懂,缺乏实践验证
使用 Spring Boot 开箱即用,但不知道底层原理

Mini-Spring 的优势

🎓 学习曲线平缓 - 从注解定义到容器实现,每一步都清晰可见
🔍 代码透明 - 没有复杂的抽象层,直接看到核心逻辑
快速上手 - 5 分钟运行示例,立即看到效果
🛠️ 可扩展 - 基于此项目可以继续添加 AOP、事务等功能


🚀 快速开始

1️⃣ 克隆项目

git clone https://gitee.com/jiucenglou/mini-spring.git
cd mini-spring

2️⃣ 编译运行

mvn clean compile
java -cp target/classes com.minispring.test.MiniSpringTest

3️⃣ 查看输出

========================================
   Mini-Spring 框架演示
========================================

【步骤 1】启动 IoC 容器,扫描包: com.minispring.example
✓ Bean 创建成功: userService (UserService)
  → 注入依赖: userService = userService
✓ Bean 创建成功: orderService (OrderService)
  → 注入依赖: orderService = orderService
✓ Bean 创建成功: userController (UserController)

【步骤 2】容器中所有 Bean:
  - userController
  - orderService
  - userService

【步骤 3】从容器中获取 Bean 并调用方法
✓ UserController 获取成功

=== 用户信息 ===
用户信息:张三,年龄 25

=== 订单信息 ===
订单信息:订单号 12345,金额 99.99

=== 业务流程 ===
创建用户: 李四
创建订单: iPhone 15
业务操作完成!

【步骤 4】验证单例模式
controller1 == controller2: true
✓ 单例模式验证通过:两次获取的是同一个实例

🔥 核心实现解析

架构概览

┌─────────────────────────────────────────┐
│          ApplicationContext              │
│           (IoC 容器核心)                  │
├─────────────────────────────────────────┤
│                                          │
│  1. ClassPathScanner 扫描包              │
│     ↓                                    │
│  2. 注册 BeanDefinition                 │
│     ↓                                    │
│  3. 反射实例化 Bean                      │
│     ↓                                    │
│  4. @Autowired 依赖注入                  │
│     ↓                                    │
│  5. 缓存单例 Bean                        │
│                                          │
└─────────────────────────────────────────┘

关键代码揭秘

📌 1. 自动扫描与注册
public ApplicationContext(String basePackage) {
    // 1. 扫描包,找到所有 @Component 类
    List<Class<?>> classes = scanner.scan(basePackage);
    
    // 2. 注册 Bean 定义
    for (Class<?> clazz : classes) {
        registerBeanDefinition(clazz);
    }
    
    // 3. 实例化所有单例 Bean
    instantiateAllSingletons();
}

工作流程:

  1. 使用 ClassPathScanner 扫描指定包路径
  2. 查找所有带有 @Component 注解的类
  3. 为每个类创建 BeanDefinition 对象
  4. 根据 @Scope 注解设置作用域(默认单例)
📌 2. 依赖注入核心逻辑
private void injectDependencies(Object instance) {
    Field[] fields = instance.getClass().getDeclaredFields();
    
    for (Field field : fields) {
        if (field.isAnnotationPresent(Autowired.class)) {
            // 打破私有字段访问限制
            field.setAccessible(true);
            
            // 根据类型获取 Bean
            Class<?> fieldType = field.getType();
            String beanName = getBeanName(fieldType);
            Object dependency = getBean(beanName);
            
            // 注入依赖
            field.set(instance, dependency);
        }
    }
}

关键点:

  • setAccessible(true) - 突破 private 限制,允许访问私有字段
  • field.set(instance, dependency) - 使用反射进行字段赋值
  • 递归注入 - 自动处理依赖链,先创建被依赖的 Bean
📌 3. 单例模式实现
// 单例缓存
private Map<String, Object> singletonBeans = new HashMap<>();

private Object createBean(BeanDefinition beanDefinition) {
    String beanName = beanDefinition.getBeanName();
    
    // 已存在则直接返回
    if (singletonBeans.containsKey(beanName)) {
        return singletonBeans.get(beanName);
    }
    
    // 创建新实例
    Class<?> clazz = beanDefinition.getBeanClass();
    Object instance = clazz.getDeclaredConstructor().newInstance();
    
    // 依赖注入
    injectDependencies(instance);
    
    // 缓存到单例池
    singletonBeans.put(beanName, instance);
    
    return instance;
}

设计思路:

  • 首次创建时实例化并缓存
  • 后续请求直接从缓存返回
  • 保证整个容器中只有一个实例

📚 使用示例

定义服务层

@Component
public class UserService {
    public String getUserInfo() {
        return "用户信息:张三,年龄 25";
    }
    
    public void createUser(String name) {
        System.out.println("创建用户: " + name);
    }
}
@Component
public class OrderService {
    public String getOrderInfo() {
        return "订单信息:订单号 12345,金额 99.99";
    }
    
    public void createOrder(String productName) {
        System.out.println("创建订单: " + productName);
    }
}

控制器注入服务

@Component
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private OrderService orderService;
    
    public void showUserInfo() {
        System.out.println("\n=== 用户信息 ===");
        System.out.println(userService.getUserInfo());
    }
    
    public void showOrderInfo() {
        System.out.println("\n=== 订单信息 ===");
        System.out.println(orderService.getOrderInfo());
    }
    
    public void businessOperation() {
        System.out.println("\n=== 业务流程 ===");
        userService.createUser("李四");
        orderService.createOrder("iPhone 15");
        System.out.println("业务操作完成!");
    }
}

启动容器

public class MiniSpringTest {
    public static void main(String[] args) {
        // 一行代码启动 IoC 容器
        ApplicationContext context = new ApplicationContext("com.example");
        
        // 获取 Bean,直接使用
        UserController controller = context.getBean(UserController.class);
        
        // 调用业务方法
        controller.showUserInfo();
        controller.showOrderInfo();
        controller.businessOperation();
    }
}

对比传统方式:

// ❌ 传统方式 - 手动管理依赖
UserService userService = new UserService();
OrderService orderService = new OrderService();
UserController controller = new UserController(userService, orderService);

// ✅ Mini-Spring - 容器自动管理
ApplicationContext context = new ApplicationContext("com.example");
UserController controller = context.getBean(UserController.class);

优势:

  • 无需手动 new 对象
  • 依赖关系自动维护
  • 便于单元测试和替换实现

🎓 学习路径

Level 1: 理解基础(30 分钟)

  • ✅ 运行测试程序,观察输出
  • ✅ 理解 @Component、@Autowired、@Scope 的作用
  • ✅ 了解 IoC 和 DI 的概念

关键问题:

  • 什么是控制反转?(把对象的创建权交给容器)
  • 什么是依赖注入?(容器自动填充依赖的对象)
  • 为什么要用单例?(节省内存,共享状态)

Level 2: 阅读源码(2 小时)

建议顺序:

  1. 注解定义annotation 包)

    • @Component - 标记组件
    • @Autowired - 自动注入
    • @Scope - 作用域配置
  2. BeanDefinition - Bean 的元数据

    • 存储 Bean 名称、类型、作用域
    • 记录实例化和初始化状态
  3. ClassPathScanner - 如何扫描类

    • 读取 classpath 下的 .class 文件
    • 解析字节码判断是否有注解
  4. ApplicationContext - 容器核心逻辑(重点!)

    • 构造函数:扫描 → 注册 → 实例化
    • createBean():反射创建对象
    • injectDependencies():依赖注入实现
    • getBean():获取 Bean 的逻辑

Level 3: 动手实验(1 天)

  • 添加新的 Service 类,体验自动扫描
  • 创建更复杂的依赖关系树(A→B→C)
  • 测试 @Scope("prototype") 的行为差异
  • 故意制造循环依赖(A→B→A),观察问题
  • 修改代码,尝试不同的注入场景

实验建议:

// 尝试原型模式
@Component
@Scope("prototype")
public class PrototypeService {
    // 每次获取都是新实例
}

// 测试
PrototypeService s1 = context.getBean(PrototypeService.class);
PrototypeService s2 = context.getBean(PrototypeService.class);
System.out.println(s1 == s2); // false

Level 4: 扩展功能(挑战)

  • 💪 构造器注入 - 支持通过构造函数传递依赖
  • 💪 @PostConstruct - Bean 初始化后的回调
  • 💪 @PreDestroy - Bean 销毁前的清理
  • 💪 循环依赖检测 - 检测并报告循环依赖
  • 💪 @Qualifier - 解决同类型多个 Bean 的歧义
  • 💪 简单 AOP - 使用动态代理实现切面

🌟 项目亮点

1. 代码极简

核心文件只有 7 个:

src/main/java/com/minispring/
├── annotation/
│   ├── Component.java      # 组件注解
│   ├── Autowired.java      # 自动注入注解
│   └── Scope.java          # 作用域注解
├── beans/
│   └── BeanDefinition.java # Bean 定义
├── context/
│   ├── ApplicationContext.java    # 容器核心 (~230 行)
│   └── ClassPathScanner.java      # 扫描器 (~80 行)
└── example/                # 示例代码
    ├── controller/
    └── service/

总代码量:不到 500 行!

2. 注释详细

每个关键步骤都有中文注释,例如依赖注入的实现:

// 【第1步】打破权限限制
// 因为字段通常是 private 的,外部无法直接访问
// setAccessible(true) 告诉 JVM:"我有特权访问这个私有字段"
field.setAccessible(true);

// 【第2步】获取字段的类型
// 例如:private UserService userService; → fieldType = UserService.class
Class<?> fieldType = field.getType();

// 【第3步】根据类型生成 Bean 名称
// 规则:类名首字母小写
// 例如:UserService.class → "userService"
String fieldBeanName = getBeanName(fieldType);

// 【第4步】从容器中获取依赖的 Bean 实例
// 这可能会递归触发依赖 Bean 的创建和注入
Object dependency = getBean(fieldBeanName);

// 【第5步】通过反射将依赖注入到字段中
try {
    // 核心操作:相当于执行 instance.field = dependency
    // 但因为是反射,所以写成 field.set(instance, dependency)
    field.set(instance, dependency);
    
    System.out.println("  → 注入依赖: " + field.getName() +
                     " = " + fieldBeanName);
} catch (IllegalAccessException e) {
    throw new RuntimeException("注入依赖失败: " + field.getName(), e);
}

3. 配套文档齐全

  • 📘 README.md - 完整的使用说明和功能介绍
  • 📗 ARCHITECTURE.md - 架构设计与工作流程详解
  • 📙 QUICKSTART.md - 5 分钟快速入门指南
  • 📕 本文档 - CSDN 推广文章

4. 真实可运行

不是伪代码,是完整可运行的项目

  • Maven 构建配置
  • 完整的示例代码
  • 测试用例验证
  • 清晰的输出日志

⚠️ 注意事项

这是一个教学用途的简化实现,与真实 Spring 框架的差距:

功能 Mini-Spring Spring Framework
依赖注入方式 仅字段注入 字段/构造器/setter 三种
作用域 单例/原型 2 种 singleton/prototype/request/session 等 7 种
AOP ✅ 完整的面向切面编程
事务管理 ✅ @Transactional
循环依赖处理 ❌ 会报错 ✅ 三级缓存解决
条件装配 ✅ @Conditional
Bean 后置处理器 ✅ BeanPostProcessor
事件机制 ✅ ApplicationEvent
国际化 ✅ MessageSource
资源加载 ✅ ResourceLoader

但这正是优势所在 - 去掉所有复杂性,只保留最核心的 IoC/DI 机制,让你专注理解本质!

学习建议:

  1. 先用 Mini-Spring 理解核心概念
  2. 再阅读 Spring 源码,会发现很多熟悉的内容
  3. 最后理解 Spring 的高级特性和设计模式

🔗 项目地址

📦 gitee: “https://gitee.com/jiucenglou/mini-spring.git”

欢迎:

  • ⭐ Star 支持 - 让更多人看到这个项目
  • 🐛 提交 Issue - 发现问题或提出建议
  • 🔧 Pull Request - 贡献代码或改进文档
  • 📢 分享给更多学习者 - 帮助他人理解 Spring

💬 常见问题

Q1: 学完这个能看懂 Spring 源码吗?

A: 能理解 60%-70% 的核心逻辑。Spring 的真实实现更复杂,包括:

  • 三级缓存解决循环依赖
  • BeanPostProcessor 扩展点
  • 各种 Aware 接口
  • FactoryBean 特殊处理

但核心思想是一样的:扫描 → 注册 → 实例化 → 注入

Q2: 为什么不实现构造器注入?

A: 为了保持代码简洁。构造器注入需要:

  • 解析构造函数参数
  • 处理参数类型匹配
  • 解决参数依赖顺序

这些会增加代码复杂度,偏离"极简"的目标。你可以作为扩展练习自己实现!

Q3: 如何处理循环依赖?

A: Mini-Spring 目前不支持循环依赖(A→B→A)。如果尝试会抛出异常。

Spring 使用三级缓存解决:

  1. singletonObjects - 完全初始化的单例
  2. earlySingletonObjects - 早期引用(未完全初始化)
  3. singletonFactories - 工厂对象

这也是一个很好的扩展练习!


🎉 总结

Mini-Spring 用最简单的方式实现了 Spring 的核心功能:

🎯 自动扫描 - 发现并注册组件
🎯 依赖注入 - 自动装配 Bean 依赖
🎯 生命周期管理 - 单例/原型模式

核心价值:

  • 📖 降低学习门槛 - 从几千行源码降到几百行
  • 🔍 透明化实现 - 没有黑盒,每行代码都可追踪
  • 快速验证 - 5 分钟运行,立即看到效果
  • 🛠️ 可扩展性强 - 在此基础上继续探索

无论你是:

  • 📖 初学者 - 想理解 Spring 原理
  • 👨‍🏫 讲师 - 需要教学示例
  • 🔧 面试官 - 考察候选人对 IoC 的理解
  • 💼 求职者 - 准备 Spring 相关面试

这个项目都能帮到你!

动手跑一遍代码,胜过读十篇理论文章! 🚀


相关文章


🙏 致谢

感谢 Spring 团队的优秀设计,让这个项目成为可能。
感谢所有为开源社区贡献的开发者们。


觉得有用?欢迎点赞、收藏、转发!有任何问题,评论区见!👇

让我们一起揭开 Spring 的神秘面纱!

作者: [绝知此事]
许可证: MIT License
最后更新: 2026年5月9日


如果你觉得这个项目对你有帮助,请给个 Star ⭐ 支持一下!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐