Fundamentals

循环依赖

了解如何在 NestJS 中处理循环依赖,包括使用前向引用和 ModuleRef 类来解决提供者和模块之间的循环依赖问题。

当两个类相互依赖时,就会发生循环依赖。例如,类 A 需要类 B,而类 B 也需要类 A。循环依赖可能在 Nest 的模块之间和提供者之间出现。

虽然应该尽可能避免循环依赖,但您并不总是能够做到这一点。在这种情况下,Nest 提供了两种方式来解决提供者之间的循环依赖。在本章中,我们描述使用前向引用作为一种技术,以及使用 ModuleRef 类从 DI 容器中检索提供者实例作为另一种技术。

我们还描述了解决模块之间循环依赖的方法。

警告 循环依赖也可能是由于使用"桶文件"/index.ts 文件来分组导入而引起的。当涉及模块/提供者类时,应该省略桶文件。例如,在与桶文件相同的目录中导入文件时不应使用桶文件,即 cats/cats.controller 不应导入 cats 来导入 cats/cats.service 文件。有关更多详细信息,请参阅此 GitHub 问题

前向引用

前向引用允许 Nest 使用 forwardRef() 实用函数引用尚未定义的类。例如,如果 CatsServiceCommonService 相互依赖,关系的双方都可以使用 @Inject()forwardRef() 实用程序来解决循环依赖。否则 Nest 不会实例化它们,因为所有必要的元数据都不可用。这是一个例子:

@@filename(cats.service)
@Injectable()
export class CatsService {
  constructor(
    @Inject(forwardRef(() => CommonService))
    private commonService: CommonService,
  ) {}
}
@@switch
@Injectable()
@Dependencies(forwardRef(() => CommonService))
export class CatsService {
  constructor(commonService) {
    this.commonService = commonService;
  }
}

提示 forwardRef() 函数从 @nestjs/common 包导入。

这涵盖了关系的一侧。现在让我们对 CommonService 做同样的事情:

@@filename(common.service)
@Injectable()
export class CommonService {
  constructor(
    @Inject(forwardRef(() => CatsService))
    private catsService: CatsService,
  ) {}
}
@@switch
@Injectable()
@Dependencies(forwardRef(() => CatsService))
export class CommonService {
  constructor(catsService) {
    this.catsService = catsService;
  }
}

警告 实例化的顺序是不确定的。确保您的代码不依赖于首先调用哪个构造函数。具有 Scope.REQUEST 的循环依赖提供者可能导致未定义的依赖项。更多信息请参见这里

ModuleRef 类替代方案

使用 forwardRef() 的替代方案是重构您的代码并使用 ModuleRef 类在(否则)循环关系的一侧检索提供者。在这里了解更多关于 ModuleRef 实用类的信息。

模块前向引用

为了解决模块之间的循环依赖,在模块关联的双方使用相同的 forwardRef() 实用函数。例如:

@@filename(common.module)
@Module({
  imports: [forwardRef(() => CatsModule)],
})
export class CommonModule {}

这涵盖了关系的一侧。现在让我们对 CatsModule 做同样的事情:

@@filename(cats.module)
@Module({
  imports: [forwardRef(() => CommonModule)],
})
export class CatsModule {}