Provider
useClass
nest 实现了 IOC 容器,会自动从入口文件扫描,分析 Module 间的引用关系,对象之间的依赖关系,将 provider 注入到目标对象里。
provider 一般是带有@Injectable修饰的 class:
ts1@Injectable() 2export class AppService { 3 getHello(): string { 4 return 'Hello World!' 5 } 6}
在 Module 的 providers 里声明:
ts1@Module({ 2 imports: [GamesModule], 3 controllers: [AppController, UserController], 4 providers: [AppService], 5 exports: [AppService], 6})
providers: [AppService]是以下写法的简写:
ts1providers: [{ provide: AppService, useClass: AppService }]
通过 provide 属性指定 token,通过 useClass 指定类,Nest 会自动实例化它并注入目标对象。
在注入时,也有多种语法,@Inject相当于constructor的语法糖:
ts1@Controller() 2export class AppController { 3 // constructor(private readonly appService: AppService) {} 4 // 与上面的代码等效 5 @Inject(AppService) 6 private readonly appService: AppService 7 8 @Get() 9 getHello(): string { 10 return this.appService.getHello() 11 } 12}
token 还可以是字符串:
ts1 providers: [{ provide: 'app-service', useClass: AppService }],
注入时需要指明:
ts1@Controller() 2export class AppController { 3 constructor(@Inject('app-service') private readonly appService: AppService) {} 4 5 @Get() 6 getHello(): string { 7 return this.appService.getHello() 8 } 9}
useValue
除了 useClass外,还能够让 IOC 容器注入固定值:
ts1@Module({ 2 controllers: [AppController, UserController], 3 providers: [ 4 { 5 provide: 'app-service', 6 useClass: AppService, 7 }, 8 { provide: 'appConfig', useValue: { name: 'NestJS' } }, 9 ], 10})
使用时,注入即可:
ts1@Controller() 2export class AppController { 3 constructor( 4 @Inject('app-service') 5 private readonly appService: AppService, 6 @Inject('appConfig') 7 private readonly appConfig: { name: string }, 8 ) {} 9 10 @Get() 11 getHello(): string { 12 return this.appService.getHello() + ' ' + this.appConfig.name 13 } 14}
useFactory
如果想要注入的值是动态的,则需要使用 useFactory
ts1@Module({ 2 controllers: [AppController, UserController], 3 providers: [ 4 { 5 provide: 'app-service', 6 useClass: AppService, 7 }, 8 { provide: 'appConfig', useValue: { name: 'NestJS' } }, 9 { 10 provide: 'dynamic', 11 useFactory: async (appService: AppService, config: { name: string }) => { 12 await new Promise((resolve) => setTimeout(resolve, 5000)); 13 return appService.getHello() + ' ' + config.name; 14 }, 15 inject: ['app-service', 'appConfig'], 16 }, 17 ], 18})
useFactory 内支持注入其他的 provide 以及支持异步,Nest 会等到 promise 获取到结果后将其注入到目标对象。
ts1@Controller() 2export class AppController { 3 constructor( 4 @Inject('dynamic') 5 private readonly getDynamic: string, 6 ) {} 7 8 @Get() 9 getHello(): string { 10 return this.getDynamic 11 } 12}
useExisting
provider 还可以通过 useExisting 来指定别名。
ts1@Module({ 2 controllers: [AppController, UserController], 3 providers: [ 4 { provide: 'appConfig', useValue: { name: 'NestJS' } }, 5 { 6 provide: 'config', 7 useExisting: 'appConfig', 8 }, 9 ], 10})
只需要通过别名注入:
ts1@Controller() 2export class AppController { 3 constructor( 4 @Inject('config') 5 private readonly config: { name: string }, 6 ) {} 7 8 @Get() 9 getHello(): string { 10 return this.config.name 11 } 12}
在某些需要兼容的情况下,useExisting 非常有用。
总结
一般情况下,provider 是通过 @Injectable 声明,然后在 @Module 的 providers 数组里注册的 class。
providers 数组有多种写法,指明 token 时,token 可以是引入的 class,也可以是字符串。
除了可以用 useClass 指定注入的 class,还可以:
- 用 useValue 指定注入的对象。
- 用 useFactory 指定动态生成的对象,同时它的参数可以是其他 provide 。
- 用 useExisting 可以起别名,给已有的 token 起一个新的 token。