Skip to content

循环依赖

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

虽然应尽可能避免循环依赖,但有时无法完全避免。在这种情况下,Nest 提供了两种方式来解决提供者之间的循环依赖。本章将介绍使用前向引用作为一种技术,以及使用 ModuleRef 类从 DI 容器中检索提供者实例作为另一种技术。

我们还将介绍如何解决模块之间的循环依赖。

警告

使用"桶文件"/index.ts 文件来分组导入时,也可能导致循环依赖。在涉及模块/提供者类时,应省略桶文件。例如,在同一目录下导入文件时不应使用桶文件,即 cats/cats.controller 不应通过导入 cats 来导入 cats/cats.service 文件。更多详情请参阅此 GitHub issue

前向引用

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

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

提示

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

以上涵盖了关系的一侧。现在让我们对 CommonService 做同样的处理:

typescript
// common.service.ts
@Injectable()
export class CommonService {
  constructor(
    @Inject(forwardRef(() => CatsService))
    private catsService: CatsService,
  ) {}
}

警告

实例化的顺序是不确定的。请确保你的代码不依赖于哪个构造函数先被调用。循环依赖中依赖 Scope.REQUEST 作用域的提供者可能导致未定义的依赖。更多信息请参阅此处

ModuleRef 类替代方案

除了使用 forwardRef() 之外,另一种方法是重构代码,使用 ModuleRef 类在(原本)循环关系的一侧检索提供者。在此处了解更多关于 ModuleRef 工具类的信息。

模块前向引用

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

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

以上涵盖了关系的一侧。现在让我们对 CatsModule 做同样的处理:

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

基于 NestJS 官方文档翻译