用于 JavaScript (Node.js) 的依赖注入容器 — Awilix
在现代 Node.js 开发中,代码架构的清晰度和可测试性变得至关重要。实现这些目标的关键工具之一就是依赖注入 (Dependency Injection, DI)。Awilix 库为 JavaScript 和 TypeScript 提供了一个强大、高性能且灵活的 DI 容器,特别适合 Node.js 服务器端应用程序。在本文中,我们将详细介绍如何使用 Awilix 来管理依赖关系、改善模块化并简化测试。
什么是依赖注入以及为什么需要容器?
依赖注入是一种设计模式,其中对象从外部获取其依赖项,而不是自己创建它们。这实现了控制反转 (Inversion of Control, IoC) 原则。容器是一种自动化创建和提供这些依赖项的工具。您无需手动创建类实例并将它们相互传递,而是在容器中描述构建规则,容器会自动解析所有依赖链。
使用 DI 容器的主要优势:
- 降低代码耦合度:模块依赖于抽象,而不是具体实现。
- 简化测试:在单元测试期间,可以轻松地将真实服务替换为模拟对象 (mock)。
- 集中管理:所有依赖配置都集中在一个地方,从而简化了重构。
- 生命周期管理:容器可以控制对象的存活时间——整个应用程序一个实例 (单例) 或每次请求一个新实例。
安装 Awilix
首先,通过 npm 安装该库:
npm install awilixAwilix 没有外部依赖,并且同时支持 CommonJS 和 ESM 模块。
Awilix 的核心概念
Awilix 围绕几个关键概念构建:
- Container (容器):存储和提供依赖项的主要对象。
- Registration (注册):将名称 (令牌) 与工厂、类或值关联起来的过程。
- Resolver (解析器):决定如何创建或获取依赖项的对象。例如,
asClass()、asFunction()、asValue()。 - Lifetime (生命周期):指定对象将被创建多少次:
ScopedLifeTime(每个作用域一次)、SingletonLifeTime(整个生命周期一次) 或TransientLifeTime(每次都新建)。
使用示例:用户服务
让我们看一个简单的例子。假设我们有一个数据库、一个用户仓库和一个用于业务逻辑的服务。
1. 定义类
// database.jsclass Database { constructor() { this.connection = 'Connected to DB'; } query(sql) { return `Result for: ${sql}`; }}
// userRepository.jsclass UserRepository { constructor({ database }) { this.db = database; } findById(id) { return this.db.query(`SELECT * FROM users WHERE id = ${id}`); }}
// userService.jsclass UserService { constructor({ userRepository }) { this.repo = userRepository; } getUser(id) { return this.repo.findById(id); }}注意构造函数:它们接受一个包含依赖项的对象。这是 Awilix 的标准做法。
2. 配置容器
const { createContainer, asClass, asValue, Lifetime } = require('awilix');
const container = createContainer();
// 注册