第 170 章:模块(Module)与依赖注入
学习目标
- 理解模块化架构
- 掌握 @Module 装饰器
- 学会依赖注入(DI)三种方式
- 理解 Provider 作用域
一、模块基础
typescript
import { Module } from '@nestjs/common';
@Module({
imports: [], // 导入其他模块
controllers: [], // 控制器
providers: [], // 服务 / 工具
exports: [], // 导出给其他模块用
})
export class UserModule {}二、特性模块(Feature Module)
bash
# 用 CLI 生成
nest g module user
nest g controller user
nest g service usersrc/
├── user/
│ ├── user.module.ts
│ ├── user.controller.ts
│ └── user.service.tstypescript
// user.module.ts
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
@Module({
controllers: [UserController],
providers: [UserService],
exports: [UserService], // 导出,让其他模块能用
})
export class UserModule {}typescript
// app.module.ts
import { UserModule } from './user/user.module';
@Module({
imports: [UserModule, OrderModule, AuthModule],
})
export class AppModule {}三、依赖注入(DI)
3.1 构造器注入(推荐)
typescript
@Controller('users')
export class UserController {
// ① 构造器注入
constructor(private readonly userService: UserService) {}
@Get()
findAll() {
return this.userService.findAll();
}
}3.2 属性注入
typescript
@Controller('users')
export class UserController {
@Inject(UserService)
private readonly userService!: UserService;
}3.3 可选依赖
typescript
import { Optional, Inject } from '@nestjs/common';
@Controller('users')
export class UserController {
constructor(
@Optional() private readonly logger?: LoggerService,
) {}
}四、Provider 类型
typescript
@Module({
providers: [
// 1. 简写
UserService,
// 2. 完整写法
{
provide: UserService,
useClass: UserService,
},
// 3. 值
{
provide: 'CONFIG',
useValue: { apiKey: 'xxx' },
},
// 4. 工厂
{
provide: 'DATABASE',
useFactory: () => ({
host: 'localhost',
port: 5432,
}),
},
// 5. 别名
{
provide: 'AliasService',
useExisting: UserService,
},
],
})
export class UserModule {}五、自定义 Provider 注入
typescript
// 注入值
constructor(@Inject('CONFIG') private config: any) {}
// 注入工厂
constructor(@Inject('DATABASE') private db: any) {}六、模块的 exports
只有 exports 数组里的 Provider 才能被其他模块用。
typescript
// user.module.ts
@Module({
providers: [UserService, UserRepository],
exports: [UserService], // ① 只导出 UserService
})
export class UserModule {}
// order.module.ts
@Module({
imports: [UserModule], // ② 导入
controllers: [OrderController],
providers: [OrderService],
})
export class OrderModule {}
// order.service.ts
@Injectable()
export class OrderService {
// ✅ 可以注入,因为 UserService 被导出
constructor(private userService: UserService) {}
}七、全局模块
typescript
@Global() // ① 全局,无需 imports
@Module({
providers: [ConfigService],
exports: [ConfigService],
})
export class ConfigModule {}typescript
@Module({
imports: [ConfigModule], // 仍要 import,但不需要每个用到的地方都 import
})
export class UserModule {}八、动态模块
typescript
@Module({})
export class DatabaseModule {
static forRoot(options: DbOptions): DynamicModule {
return {
module: DatabaseModule,
providers: [
{
provide: 'DB_OPTIONS',
useValue: options,
},
DatabaseService,
],
exports: [DatabaseService],
global: true,
};
}
}typescript
// app.module.ts
@Module({
imports: [
DatabaseModule.forRoot({
host: 'localhost',
port: 5432,
}),
],
})
export class AppModule {}九、Provider 作用域
typescript
// 默认:单例
@Injectable()
export class UserService {}
// 请求作用域(每个请求一个新实例)
@Injectable({ scope: Scope.REQUEST })
export class UserService {}
// 瞬态(每次注入都新实例)
@Injectable({ scope: Scope.TRANSIENT })
export class UserService {}十、循环依赖
typescript
// a.service.ts
@Injectable()
export class AService {
constructor(@Inject(forwardRef(() => BService)) private b: BService) {}
}
// b.service.ts
@Injectable()
export class BService {
constructor(@Inject(forwardRef(() => AService)) private a: AService) {}
}十一、本章小结
| 概念 | 关键 |
|---|---|
| @Module | 装饰器定义模块 |
| imports | 引入其他模块 |
| providers | 注册可注入类 |
| exports | 让其他模块用 |
| @Global | 全局模块 |
| 构造器注入 | 推荐方式 |
| 循环依赖 | forwardRef |
动手练习
- 创建 user / order 两个模块
- 在 order 中使用 user 的 Service
- 创建一个全局 ConfigModule
- 用 useFactory 注入一个配置对象