Provider

useClass

nest 实现了 IOC 容器,会自动从入口文件扫描,分析 Module 间的引用关系,对象之间的依赖关系,将 provider 注入到目标对象里。

provider 一般是带有@Injectable修饰的 class:

ts
1@Injectable()
2export class AppService {
3  getHello(): string {
4    return 'Hello World!'
5  }
6}

在 Module 的 providers 里声明:

ts
1@Module({
2  imports: [GamesModule],
3  controllers: [AppController, UserController],
4  providers: [AppService],
5  exports: [AppService],
6})

providers: [AppService]是以下写法的简写:

ts
1providers: [{ provide: AppService, useClass: AppService }]

通过 provide 属性指定 token,通过 useClass 指定类,Nest 会自动实例化它并注入目标对象。

在注入时,也有多种语法,@Inject相当于constructor的语法糖:

ts
1@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 还可以是字符串:

ts
1 providers: [{ provide: 'app-service', useClass: AppService }],

注入时需要指明:

ts
1@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 容器注入固定值:

ts
1@Module({
2  controllers: [AppController, UserController],
3  providers: [
4    {
5      provide: 'app-service',
6      useClass: AppService,
7    },
8    { provide: 'appConfig', useValue: { name: 'NestJS' } },
9  ],
10})

使用时,注入即可:

ts
1@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

ts
1@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 获取到结果后将其注入到目标对象。

ts
1@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 来指定别名。

ts
1@Module({
2  controllers: [AppController, UserController],
3  providers: [
4    { provide: 'appConfig', useValue: { name: 'NestJS' } },
5    {
6      provide: 'config',
7      useExisting: 'appConfig',
8    },
9  ],
10})

只需要通过别名注入:

ts
1@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。