我们仔细看看 @Module() 装饰器。在 user.module 中,我们声明:
@Module({
controllers: [UserController],
providers: [UserService],
})providers 属性采用数组的形式提供。到目前为止,我们已经通过类名列表提供了这些提供者。事实上,语法 providers: [UserService] 是更完整语法的简写如下:
providers: [
{
provide: UserService,
useClass: UserService,
},
];既然我们看到了这个明确的结构,我们就可以理解注册过程了。在这里,我们明确地将令牌 UserService 与类 UserService 相关联。简写符号只是为了简化最常见的用例,其中令牌provider 用于请求具有相同名称的类的实例。
如果要求超出标准提供者时会发生什么?这里有一些例子:
想要创建一个自定义实例而不是让 Nest 实例化(或返回一个类的缓存实例)
想在第二个依赖项中重用现有的类
想用模拟版本覆盖一个类以进行测试
Nest 允许定义自定义提供者来处理这些情况。它提供了几种定义自定义提供者的方法。如下:
第一:userValue
useValue 语法对于注入常量值、将外部库放入 Nest 容器或用模拟对象替换真实实现非常有用。假设您想强制 Nest 使用模拟的 CatsService 进行测试。
import { CatsService } from './cats.service';
const mockCatsService = {
/* mock implementation
...
*/
};
@Module({
imports: [CatsModule],
providers: [
{
provide: CatsService,
useValue: mockCatsService,
},
],
})
export class AppModule {}在此示例中,CatsService 令牌将解析为 mockCatsService 模拟对象。 useValue 需要一个值——在本例中是一个文字对象,它与它要替换的 CatsService 类具有相同的接口。由于 TypeScript 的结构类型,可以使用任何具有兼容接口的对象,包括文字对象或用 new 实例化的类实例。
第二:useClass
除了上述的例子,如果useClass还能动态的解析令牌所指向的类,比如:
const configServiceProvider = {
provide: ConfigService,
useClass:
process.env.NODE_ENV === 'development'
? DevelopmentConfigService
: ProductionConfigService,
};
@Module({
providers: [configServiceProvider],
})
export class AppModule {}可以看到,configServiceProvider最后指定的类是动态的被指定,可以是DevelopmentConfigService ,也可以是ProductionConfigService。
第三:非class
有时,我们可能希望灵活地使用字符串或符号作为 DI 令牌:
import { connection } from './connection';
@Module({
providers: [
{
provide: 'CONNECTION',
useValue: connection,
},
],
})
export class AppModule {}在此示例中,我们将字符串值标记 ('CONNECTION') 与我们从外部文件导入的预先存在的连接对象相关联。
第四:useFactory
useFactory 允许动态创建提供者。实际提供者将由工厂函数返回的值提供。工厂函数可以根据需要简单或复杂。一个简单的工厂可能不依赖于任何其他提供者。更复杂的工厂本身可以注入所需的其他提供者的结果。对于后一种情况,工厂提供者语法有一对相关的机制:
a. 工厂函数可以接受(可选)参数。
b. inject 属性(可选的)接受一组提供者,Nest 将在实例化过程中解析这些提供者并将其作为参数传递给工厂函数。此外,这些提供者可以标记为可选。这两个列表应该是相关的:Nest 将以相同的顺序将注入列表中的实例作为参数传递给工厂函数。下面的示例演示了这一点。
const connectionProvider = {
provide: 'CONNECTION',
useFactory: (optionsProvider: OptionsProvider, optionalProvider?: string) => {
const options = optionsProvider.get();
return new DatabaseConnection(options);
},
inject: [OptionsProvider, { token: 'SomeOptionalProvider', optional: true }],
// \_____________/ \__________________/
// This provider The provider with this
// is mandatory. token can resolve to `undefined`.
};
@Module({
providers: [
connectionProvider,
OptionsProvider,
// { provide: 'SomeOptionalProvider', useValue: 'anything' },
],
})
export class AppModule {}第五:useExisting
useExisting 允许为现有提供者创建别名。
@Injectable()
class LoggerService {
/* implementation details */
}
const loggerAliasProvider = {
provide: 'AliasedLoggerService',
useExisting: LoggerService,
};
@Module({
providers: [LoggerService, loggerAliasProvider],
})
export class AppModule {}有时,应延迟应用程序启动,直到完成一个或多个异步任务。例如,可能不想在与数据库建立连接之前开始接受请求。可以使用异步提供程序实现此目的。
{
provide: 'ASYNC_CONNECTION',
useFactory: async () => {
const connection = await createConnection(options);
return connection;
},
}总结一下:所有服务Service 或者提供者Providers 都应该用@Injectable() 修饰标注,表示自己可以作为依赖被注入到其他的类中,这样才能在module 注册入Ioc容器中。
www.haizhuan.tk