跳到主要内容

循环依赖

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

虽然应尽量避免循环依赖,但有时是不可避免的。 在这种情况下,Nest 提供两种解决提供者之间循环依赖的方法。 在本章中,我们将描述使用前向引用作为一种技术, 以及使用ModuleRef类从 DI 容器中检索提供者实例的另一种方法。

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

注意

循环依赖可能也是使用 "barrel 文件" 或index.ts文件对导入进行分组时的原因。在涉及模块/提供者类时,不应使用 barrel 文件。例如,在 barrel 文件所在目录中导入文件时,即 cats/cats.controller 不应导入 cats/cats.service 文件时,不应使用 barrel 文件。有关详细信息,请参见此 GitHub 问题。

前向引用

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

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

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

这涵盖了关系的一侧。现在让我们对 CommonService 采取相同的方法:

@Injectable()
export class CommonService {
constructor(
@Inject(forwardRef(() => CatsService))
private 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 {}