生命周期守卫
InferDI 提供三种生命周期:
| 种类 | 创建时机 | 缓存于 | 由容器释放 |
|---|---|---|---|
singleton | 每个拥有它的容器创建一次 | 拥有它的容器 | 是 |
scoped | 每个作用域创建一次 | 作用域 | 是 |
transient | 每次解析都创建 | 从不缓存 | 否 |
生命周期规则
单例不能直接依赖 scoped 或 transient 服务。单例只创建一次并在每个请求间共享,因此如果它捕获了一个作用域级的值——当前请求的上下文、用户或事务——那么这一个请求的状态就会悄无声息地泄漏到所有其他请求中。InferDI 让这种边界情况在类型系统中无法被表达出来,而不是把它留给代码评审。
ts
new Container()
.registerClass('request', RequestContext, [], 'scoped')
.registerClass('users', UserService, ['request'], 'singleton')该注册会被 TypeScript 拒绝。在严格模式下,如果有类型转换绕过了类型系统,同样的形态在运行时也会被拒绝。
严格模式
strict: true 是默认值。它能捕捉:
- 由类型转换引入的单例到作用域级或单例到瞬态的违规
- 捕获外层容器的工厂泄漏
- 同步单例循环
- 同步瞬态循环
- 绕过静态检查的动态键误用
ts
const root = new Container({ strict: true })快速模式
仅在测试已证明依赖图形态之后才使用 strict: false:
ts
const root = new Container({ strict: false })快速模式会从解析路径中移除运行时的循环与生命周期记账。它不会改变类型层面的契约,但也无法防御不诚实的类型转换、捕获的外层容器或循环。
推荐的工作流程:在严格模式下开发和测试,然后仅在审计之后才为性能敏感的生产环境依赖图切换模式。
