一、核心概念解析
1.1 依赖注入(DI)的本质
依赖注入是一种设计模式,通过 IoC(控制反转)容器管理对象生命周期。在 NestJS 中,@Injectable()
标记的类会被容器管理,而 @Inject()
用于显式指定依赖项。
1.2 @Injectable()
vs @Inject()
@Injectable()
:标记类为可注入的提供者(Provider),告知 NestJS 容器需要管理该类的实例。@Inject(token)
:显式指定依赖项的注入令牌(Token),用于非类型安全的注入场景。
二、基础用法
2.1 构造函数注入(推荐)
// service.ts
import { Injectable } from '@nestjs/common';@Injectable()
export class LoggerService {log(message: string) {console.log(`[Logger] ${message}`);}
}// user.service.ts
import { Injectable, Inject } from '@nestjs/common';
import { LoggerService } from './logger.service';@Injectable()
export class UserService {// 自动解析 LoggerService 类型constructor(private readonly logger: LoggerService) {}getUser() {this.logger.log('Fetching user...');return { id: 1, name: 'John' };}
}
2.2 属性注入(特殊场景)
import { Injectable, Inject } from '@nestjs/common';@Injectable()
export class HttpService<T> {@Inject('HTTP_OPTIONS') // 显式指定令牌private readonly httpClient: T;
}
三、高级用法:自定义提供者
3.1 值提供者(Value Provider)
// app.module.ts
import { Module } from '@nestjs/common';@Module({providers: [{provide: 'API_KEY', // 自定义令牌useValue: '12345-ABCDE', // 静态值},],
})
export class AppModule {}// 使用场景
@Injectable()
export class ConfigService {@Inject('API_KEY') private readonly apiKey: string;
}
3.2 工厂提供者(Factory Provider)
// app.module.ts
import { Module } from '@nestjs/common';@Module({providers: [{provide: 'DATABASE_CONNECTION',useFactory: async () => {const config = await getConfig(); // 异步操作return new DatabaseConnection(config);},inject: [ConfigService], // 注入其他依赖},],
})
export class AppModule {}
3.3 类提供者(Class Provider)
// app.module.ts
import { Module } from '@nestjs/common';
import { CacheService } from './cache.service';@Module({providers: [{provide: 'CACHE_MANAGER',useClass: CacheService, // 指定具体类},],
})
export class AppModule {}
四、常见问题
4.1 依赖未解析错误
错误:Nest can't resolve dependencies of the UserService (?)
原因:依赖项未在模块的 providers
中注册。
解决方案:
@Module({providers: [UserService, LoggerService], // 确保所有依赖已注册
})
export class UserModule {}
4.2 循环依赖
场景:ServiceA
依赖 ServiceB
,同时 ServiceB
依赖 ServiceA
。
解决方案:使用 forwardRef
结合 @Inject
:
// service-a.ts
import { forwardRef, Inject } from '@nestjs/common';@Injectable()
export class ServiceA {constructor(@Inject(forwardRef(() => ServiceB)) private serviceB: ServiceB) {}
}// service-b.ts
import { forwardRef, Inject } from '@nestjs/common';@Injectable()
export class ServiceB {constructor(@Inject(forwardRef(() => ServiceA)) private serviceA: ServiceA) {}
}
五、最佳实践
- 优先构造函数注入:明确依赖关系,提升代码可读性。
- 避免滥用
@Inject
:仅在非类型安全场景(如字符串令牌)时使用。 - 模块化设计:通过
providers
数组集中管理依赖,便于维护。 - 合理使用自定义提供者:解耦配置与业务逻辑,提升灵活性。
六、总结
@Inject
是 NestJS 依赖注入系统的关键工具,通过显式指定依赖令牌,解决了非类型安全场景下的注入问题。结合 @Injectable
和自定义提供者,可以构建出高可维护、松耦合的后端应用。