依赖注入系统循环依赖问题类型及检测实现方案咨询
依赖注入库的循环依赖检测与防护方案
核心检测思路
不管是单例还是factory类型的绑定,核心都是追踪当前线程的对象创建调用栈:在每次调用Provider.get()时,先检查目标类型是否已经存在于当前线程的创建栈中。如果存在,说明出现了循环依赖,直接抛出异常并带上完整的循环链;如果不存在,则将该类型压入栈,执行对象创建逻辑,完成后再从栈中弹出。这种方式能在循环导致死锁或栈溢出前就终止流程,同时给出明确的错误提示。
针对两类典型循环的具体实现
1. 单例间的循环依赖(死锁风险)
对于单例绑定,除了原有同步锁保证初始化原子性外,需要为每个线程维护一个线程本地的初始化栈:
- 当尝试获取单例的
get()时,先检查目标类型是否在当前线程的初始化栈内- 若存在,抛出异常,示例错误信息:
循环依赖检测到:A → B → A - 若不存在,将该类型压入栈,然后进入同步块执行初始化逻辑
- 初始化完成后,将类型从栈中弹出,释放同步锁
- 若存在,抛出异常,示例错误信息:
- 这种方式会在死锁发生前就阻断流程,避免线程挂起。
2. Factory间的循环依赖(栈溢出风险)
对于factory绑定,每次调用get()时都要初始化一个临时的创建栈(或复用线程本地栈,每次调用前清空):
- 创建对象前,检查当前类型是否在栈中
- 若存在,抛出异常并展示循环链
- 若不存在,压栈后执行构造注入逻辑,完成后弹栈
- 由于factory每次都会生成新实例,递归创建会快速触发栈溢出,提前检测能在溢出前终止并给出清晰提示。
是否存在第三类递归问题?
存在混合场景的循环依赖,无法单纯归为上述两类,但同样可以用调用栈追踪的方式检测:
- 单例与factory混合循环:比如单例A在初始化时调用factory创建B,而B的构造又依赖A。示例代码:
@Singleton class A(val bProvider: Provider<B>) { init { bProvider.get() } } class B(val a: A)
- 间接循环依赖:比如A依赖B,B依赖C,C又依赖A,这类跨多层的循环,不管绑定类型如何组合,调用栈都会记录完整的依赖链,触发检测逻辑。
这些场景本质都是依赖链出现了闭环,核心检测逻辑完全覆盖,不需要额外的特殊处理。
内容的提问来源于stack exchange,提问作者underflow
相关产品推荐
相关产品推荐

