【NestJS】依赖注入:超越自动new的深层解析

NestJS 依赖注入(DI)全解析:不仅仅是 new 一下

你对 NestJS 或 Angular 的依赖注入系统的直觉非常准:

"看起来不过就是自动 new 一下类而已。"

没错,从表面上看确实如此,但 NestJS DI 的底层机制和设计哲学远比这复杂,它不仅仅是自动创建对象,而是一套完整的 对象生命周期管理、依赖解耦和模块化架构系统

下面我从 表象 → 原理 → 设计模式 → 实际威力 四个层次来讲透。


🧩 一、表象层:@Injectable() 只是标记可注入对象

ts 复制代码
@Injectable()
export class UserService {
  constructor(private db: DatabaseService) {}
}

看起来,Nest 启动时只是做了几件事:

  1. 检查 UserService 是否带有 @Injectable()
  2. 自动创建它的实例;
  3. 解析构造函数参数;
  4. 把依赖的 DatabaseService 注入进去。

表面上看就是帮你 new UserService(new DatabaseService()),但它背后是一个自动、可扩展、可替换、可分层的对象创建系统


⚙️ 二、原理层:NestJS DI 容器 = IoC + 依赖图解析器

Nest 的 DI 核心是 IoC 容器(Inversion of Control Container)

它本质上是 "对象工厂 + 依赖图解析器"

ts 复制代码
// 传统写法:
const userService = new UserService(new DatabaseService());

// Nest DI:
const userService = container.resolve(UserService);

容器会自动:

  • 扫描所有 @Injectable() 类;
  • 构建依赖图,解析依赖关系;
  • 递归实例化依赖对象;
  • 管理对象生命周期(单例、请求作用域、瞬时);
  • 支持替换依赖(不同环境或 mock)。

这就是"控制反转(IoC)"的真正含义:业务对象不负责创建依赖,由容器统一管理。


🧱 三、设计模式层:DI 是多种经典模式的集合

模式 DI 中体现
工厂模式(Factory) 容器负责对象创建
单例模式(Singleton) 默认 provider 全局单例
策略模式(Strategy) 不同 token 提供不同实现
装饰器模式(Decorator) @Injectable()@Module() 保存元信息
依赖反转原则(DIP) 上层依赖抽象接口而非具体实现

💡 实例:切换实现而不改业务代码

ts 复制代码
export abstract class Logger {
  abstract log(message: string): void;
}

@Injectable()
export class ConsoleLogger implements Logger {
  log(msg: string) { console.log(msg); }
}

@Injectable()
export class FileLogger implements Logger {
  log(msg: string) { writeFileSync('log.txt', msg); }
}

// 模块中切换实现:
@Module({
  providers: [{ provide: Logger, useClass: FileLogger }],
})
export class AppModule {}

业务层依赖 Logger 抽象类,最终实现由模块配置决定。

这就是 依赖反转 + 策略模式 的威力。


🧠 四、实际威力:可扩展、可测试、可替换、可插拔

1️⃣ 可扩展性

模块化系统通过 provider 注入,实现插件式扩展:

ts 复制代码
@Module({
  imports: [DatabaseModule, UserModule, AuthModule],
})
export class AppModule {}

模块之间解耦,可随时增删。

2️⃣ 可测试性

单元测试时轻松 mock 依赖,无需改业务代码:

ts 复制代码
const moduleRef = await Test.createTestingModule({
  providers: [
    UserService,
    { provide: DatabaseService, useValue: mockDb },
  ],
}).compile();

3️⃣ 可插拔架构

开发支付模块:

ts 复制代码
export abstract class PaymentProvider {
  abstract pay(): Promise<void>;
}

不同 app 只需注册不同实现:

ts 复制代码
{ provide: PaymentProvider, useClass: WechatPay }
{ provide: PaymentProvider, useClass: StripePay }

上层逻辑完全不变。

4️⃣ 生命周期控制

Nest 可控制实例作用域:

Scope 描述
DEFAULT 全局单例
REQUEST 每个请求新实例
TRANSIENT 每次注入都新实例

既高效(单例),又能隔离(请求作用域)。


相关推荐
死神6543 天前
Vibe Coding 实战:我用 Nuxt 3+NestJS 做了一个电商运营 SaaS
saas·nestjs·nuxt.js·电商运营·vibe coding
copyer_xyf5 天前
PostgreSQL 做向量检索:一条单库路线
数据库·agent·nestjs
Revolution615 天前
Nest.js 是什么:怎样用它写出第一个后端接口
后端·node.js·nestjs
濮水大叔7 天前
NestJS 与 CabloyJS 的 env/config 架构对比:从环境变量到实例级配置
前端·node.js·nestjs
Allshadow12 天前
Nest.js - 连接数据库
javascript·nestjs
Allshadow15 天前
Nest.js - 目录结构
javascript·nestjs
南一Nanyi16 天前
依赖注入和控制反转
前端·设计模式·nestjs
晓得迷路了17 天前
栗子前端技术周刊第 138 期 - NestJS 12、Astro 7.1、npm 12...
前端·javascript·nestjs
xiaofeichaichai19 天前
NestJS 实战:JWT 登录认证、Token 刷新与安全退出
redis·nestjs·jwt·twitter
无双_Joney21 天前
[四期 - 4] NestJS专栏 - 征召一下大家的意见,非常想知道大家都想看看Nest的哪些方面
前端·后端·nestjs