第 5 讲:依赖注入与 Service 注册机制

DeepSeek Harness 源码解析 · 第 5 讲

DeepSeek Harness 源码解析系列 第 5 讲:依赖注入与 Service 注册机制 基于 DeepSeek Harness 源码 · 2026-08-20 引言 前一讲我们深入了 Turn flow 和 Agent 生命周期,看到了 Fiber 如何通过 epoch 驱动插件的加载与卸载。但一个关键问题还没回答:Fiber 内部那些 ctx.events 、 ctx.logger 、 ctx.storage 等服务是怎么被注册和解析的? 这就是 Cordis 的依赖注入系统——它不是 Spring 那种 XML 配置的 DI,而是一个基于 Fiber 作用域、epoch 驱动的响应式服务容器。每个插件声明它需要什么,容器在运行时自动注入,依赖变化时自动重载。今天我们就来拆解这套机制。 一、核心概念:Reflect 服务注册表 Cordis 的 DI 系统由 Reflect 服务提供,它是所有 Service 注册、解析和通知的入口。整个系统围绕三个核心数据结构运转: 📄 vendor/cordis/src/reflect.ts(第 1-30 行) // Reflect 服务构造函数 —— 服务的注册与解析中枢 export class ReflectService { // 注册表:symbol → Impl 记录,每个 Impl 持有服务的 name、value、fiber 引用 // 使用 symbol 作为 key 避免命名冲突,支持跨插件的服务发现 store: Dict<Impl> = Object.create(null) // 监听表:symbol → 回调数组,当服务状态变化时通知所有订阅者 // 实现"依赖变化 → 消费者自动响应"的响应式模式 listeners: Dict<Array<() => void>> = Object.create(null) // 拦截配置:service name → intercept config,来自插件的 inject 声明 // 当插件 A 声明 inject { logger: { level: 3 } } 时,这里存储了 A 的自定义配置 intercept: Dict<any> = Object.create(null) constructor(ctx: Context) { // 将当前 fiber 的 inject 声明合并到 intercept 配置中 // 这样每个插件可以覆盖上游服务的默认行为 if (ctx.fiber.inject) { this.intercept = ctx[Context.intercept] } } Reflect 的核心职责可以概括为三个动词: 注册 (register)、 解析 (resolve)、 通知 (notify)。我们一个一个来看。 二、服务注册:register 方法 当一个插件想要向容器提供服务时,它调用 ctx.reflect.register() : 📄 vendor/cordis/src/reflect.ts(第 46-91 行) // 注册一个服务 —— 其他插件可以通过这个名字找到它 // name: 服务名(如 "events"、"logger"、"storage") // value: 服务实例本身 // options: 可选配置 register(name: string, value: any, options: RegisterOptions = {}) { // 生成一个唯一的 symbol 作为这个服务的标识符 // Symbol.for() 保证全局唯一,即使不同插件注册同名服务也不会冲突 const key = Symbol.for(name) // 如果这个服务已经注册过,先检查是否允许覆盖 // options.override 为 true 时才允许替换已有服务 // 这防止了插件意外覆盖核心服务的 bug const existing = this.store[key] if (existing && !options.override) { throw new CordisError(`SERVICE_ALREADY_REGISTERED: ${name}`)…

阅读全文:DeepSeek Harness 源码解析 第 5 讲