1. 从零实现一个轻量级依赖注入容器
最近在重构一个老项目时,发现代码里到处都是直接new对象的硬编码,各种服务类之间形成了蜘蛛网般的依赖关系。这让我下定决心实现一个轻量级的依赖注入容器,以下是整个实现过程中的技术思考和实战记录。
依赖注入(Dependency Injection)作为控制反转原则的具体实现,能够有效解耦组件依赖关系。但像Spring这样的全功能框架对于小型项目来说过于沉重,我们需要一个不到200行代码的轻量级解决方案。这个容器需要支持构造函数注入、单例管理、循环依赖检测等核心功能,同时保持极简的API设计。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心设计思路
2.1 容器基础结构
容器最核心的功能就是管理对象的创建和生命周期。我们用一个Map来存储所有Bean的定义:
typescript复制class DIContainer {
private beans = new Map<string, BeanDefinition>();
register(name: string, definition: BeanDefinition) {
this.beans.set(name, definition);
}
get<T>(name: string): T {
// 解析实现...
}
}
每个BeanDefinition需要包含以下信息:
- 构造函数或工厂函数
- 是否是单例标志
- 依赖参数列表
- 已创建的实例(单例模式下)
2.2 依赖解析算法
当请求一个Bean时,容器需要递归解析所有依赖项。这个过程需要注意几个关键点:
- 构造函数参数分析:通过反射获取构造函数的参数类型
- 循环依赖检测:维护一个正在创建的Bean栈,发现循环立即报错
- 单例处理:如果是单例且已创建,直接返回现有实例
typescript复制private resolvingStack = new Set<string>();
get<T>(name: string): T {
if (this.resolvingStack.has(name)) {
throw new Error(`发现循环依赖: ${[...this.resolvingStack, name].join(' -> ')}`);
}
this.resolvingStack.add(name);
try {
const definition = this.beans.get(name);
// 实际创建逻辑...
} finally {
this.resolvingStack.delete(name);
}
}
