循环依赖 Circular dependency

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

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

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

警告

使用barrel 文件/index.ts 文件对导入进行分组时也可能导致循环依赖。当涉及模块/提供程序类时,应省略 barrel 文件。例如,在导入与 barrel 文件位于同一目录中的文件时不应使用 barrel 文件,即 cats/cats.controller 不应导入 cats 来导入 cats/cats.service 文件。有关更多详细信息,另请参阅 此 github 问题

前向引用

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

ts
TS
ts
// cats.service
@Injectable()
export class CatsService {
  constructor(
    @Inject(forwardRef(() => CommonService))
    private commonService: CommonService,
  ) {}
}
提示

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

这涵盖了关系的一侧。现在让我们对 CommonService 执行相同操作:

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

实例化的顺序不确定。请确保您的代码不依赖于首先调用哪个构造函数。循环依赖关系依赖于具有 Scope.REQUEST 的提供程序可能会导致未定义的依赖关系。更多信息可在此处获得此处

ModuleRef 类替代方案

使用 forwardRef() 的替代方法是重构代码并使用 ModuleRef 类检索(否则)循环关系一侧的提供程序。了解有关 ModuleRef 实用程序类的更多信息此处

模块前向引用

为了解决模块之间的循环依赖关系,请在模块关联的两侧使用相同的 forwardRef() 实用程序函数。例如:

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

这涵盖了关系的一方面。现在让我们对CatsModule进行同样的操作:

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