如何解决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
相关产品推荐
相关产品推荐

