You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决Angular Provider中故意设置的循环依赖问题?

问题

需求:对于该Provider,希望优先使用已存在的InjectionToken,若不存在则使用指定的Provider。

现有代码如下:

providers: [
    {
        provide: DesignerRecoveryComponentStore,
        useFactory: (injector: Injector) => {
            return injector.get(
                DesignerRecoveryComponentStore,
                new DesignerRecoveryComponentStore(
                    injector.get(Store),
                    injector.get(Actions),
                    injector.get(NgZone),
                    injector.get(DesignerActionDispatcher),
                    injector.get(PresentationRecoveryService),
                    injector.get(Router),
                    injector.get(PadsDialogService)
                )
            );
        },
        deps: [Injector]
    }
]

这段代码意图是检查是否存在DesignerRecoveryComponentStore(子类)的Provider,存在则返回该实例,不存在则创建主类的新实例。但目前出现循环依赖错误,尝试将查找的类替换为接口后编译失败,请问如何正确实现该需求?

解决方案

1. 循环依赖的根源

当前代码中,injector.get(DesignerRecoveryComponentStore)会触发当前Provider的工厂函数重复执行——因为Angular查找该token时,当前的Provider就是它的唯一注册项,进而引发无限递归,导致循环依赖错误。

2. 正确实现步骤

(1)定义独立的InjectionToken作为标识

不要直接用类本身作为查找token,创建专门的InjectionToken标记“自定义实现”:

export const DESIGNER_RECOVERY_COMPONENT_STORE = new InjectionToken<DesignerRecoveryComponentStore>(
  'DesignerRecoveryComponentStore'
);

(2)调整Provider注册逻辑

  • 先注册默认的主类实现
  • 允许子类通过上述InjectionToken提供自定义实现
  • 最终对外暴露的Provider优先取自定义实现,无自定义时用默认:
providers: [
  // 注册默认实现
  {
    provide: DesignerRecoveryComponentStore,
    useClass: DesignerRecoveryComponentStore,
    deps: [Store, Actions, NgZone, DesignerActionDispatcher, PresentationRecoveryService, Router, PadsDialogService]
  },
  // 注册可覆盖的token,优先使用自定义实现
  {
    provide: DESIGNER_RECOVERY_COMPONENT_STORE,
    useFactory: (injector: Injector) => {
      return injector.get(DESIGNER_RECOVERY_COMPONENT_STORE, injector.get(DesignerRecoveryComponentStore));
    },
    deps: [Injector]
  }
]

(3)子类自定义实现的注册方式

如果有子类需要替换默认实现,只需注册DESIGNER_RECOVERY_COMPONENT_STORE这个token:

// 子类示例
class CustomDesignerRecoveryComponentStore extends DesignerRecoveryComponentStore {
  constructor(
    store: Store,
    actions: Actions,
    ngZone: NgZone,
    dispatcher: DesignerActionDispatcher,
    recoveryService: PresentationRecoveryService,
    router: Router,
    dialogService: PadsDialogService
  ) {
    super(store, actions, ngZone, dispatcher, recoveryService, router, dialogService);
    // 自定义逻辑
  }
}

// 注册自定义实现
providers: [
  {
    provide: DESIGNER_RECOVERY_COMPONENT_STORE,
    useClass: CustomDesignerRecoveryComponentStore,
    deps: [Store, Actions, NgZone, DesignerActionDispatcher, PresentationRecoveryService, Router, PadsDialogService]
  }
]

(4)使用时的注入方式

在需要使用的地方,注入DESIGNER_RECOVERY_COMPONENT_STORE即可自动获取优先的自定义实现:

constructor(
  @Inject(DESIGNER_RECOVERY_COMPONENT_STORE)
  private recoveryStore: DesignerRecoveryComponentStore
) {}

3. 为什么不能用接口?

Angular依赖注入基于运行时token,而TypeScript接口编译后会被擦除,无法作为DI的标识使用。必须使用类、InjectionToken或字符串(不推荐)作为DI token。

内容的提问来源于stack exchange,提问作者Tane van Wifferen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 09:17:14