【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 每次注入都新实例

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


相关推荐
爱丶不疚4 天前
Electron:Module 与 Service 的职责边界与加载时序编排
前端·electron·nestjs
沐沐师13 天前
Nest.js 微服务入门教程
微服务·nestjs
沐沐师13 天前
Redis 入门教程
redis·nestjs
谷无姜13 天前
QuizForge:在不停踩坑后的技术决策复盘
前端·nestjs
Flynt15 天前
NestJS 12升级踩坑:从Webpack到Rspack,我折腾了一整个周末
typescript·node.js·nestjs
张洪权16 天前
nest.js websocket 群聊----私聊功能
前端·nestjs
用户712828968446717 天前
NestJS 发布v12版本,看一下有哪些改动
nestjs
YIAN22 天前
NestJS 入门核心梳理:模块化架构、装饰器与依赖注入
后端·nestjs
半个落月23 天前
NestJS 入门实战:从工厂模式到 Todo CRUD,讲透模块化、依赖注入与测试
后端·nestjs
浮生望23 天前
NestJS MVC 实战:模块化架构、CRUD 全流程与标准化错误处理
nestjs