资讯动态

Tego 依赖注入(DI)容器实战:快速看懂核心代码的 5 个设计思想

发布时间:2026/10/15 18:19:58 来源:尧图企业网站定制
低代码后端前端插件系统【免费下载链接】tegoTego 是一个可插拔的 Node.js 框架用于构建可定制的开发平台。它使开发者能够创建自己的无代码/低代码系统或事件驱动型应用程序且框架核心侧重于稳定性与对不同运行环境的适配。项目地址https://gitcode.com/tegojs/tego点击查看免费下载Tego 是一个可插拔的 Node.js 框架其依赖注入DI容器位于独立包tachybase/di源码在 packages/di/是整个框架插件化、低代码能力的底座。本文不堆代码而是带你从核心源码的设计思想出发快速理解这个容器为什么这样写以及Service、Inject这些装饰器背后到底发生了什么。一、为什么 Tego 需要一个依赖注入容器一句话解耦。Tego 的核心功能由一个个插件组成插件之间、插件与框架之间互相需要引用比如控制器需要拿到 Application 实例、Action 注册表。如果每个插件都去new别人就会形成硬编码的依赖网改一处崩一片。DI 容器的做法是服务自己注册每个可注入的类通过Service()装饰器声明我可以被注入消费方只声明我要什么通过Inject()指定要哪个服务由容器在实例化时自动填好实例由容器统一创建和管理单例、生命周期、销毁都由容器负责。这样插件之间只面向标识符类名、字符串或 Token编程完全解耦。二、核心概念先认识 4 个关键角色 角色所在文件职责ContainerInstancecontainer-instance.class.ts容器主体负责注册、查找、创建、销毁服务Container同上文件末尾别名默认全局容器日常最常用ContainerRegistrycontainer-registry.class.ts容器登记处管理所有按 id 创建的容器Tokentoken.class.ts类型化服务标识符适合只有接口没有类的场景其中Container就是ContainerInstance.default的别名。容器采用惰性单例第一次访问ContainerInstance.default时才创建并注册进 Registry见 container-instance.class.ts#L43-L49。这个设计避免了模块加载顺序问题——你不必担心谁先 import 谁。2.1 服务的三种标识方式容器的get/set接收的标识符ServiceIdentifier有三种对应不同使用场景定义见 service-identifier.type.ts类本身container.set({ type: MyService })—— 最常用字符串 idcontainer.set({ id: actions, type: X })—— 一个 id 下可挂多个实例Tokennew TokenMyInterface(plugin)—— 面向接口编程测试和替换实现时非常方便。三、设计思想 1用元数据 空值哨兵实现惰性初始化打开核心文件 container-instance.class.ts你会发现容器里并没有直接存服务实例而是存了一个metadataMapMapServiceIdentifier, ServiceMetadata。每个服务的完整档案定义在 service-metadata.interface.tsid、type、factory、value、scope、multiple、eager等字段。而实例还没创建这一状态用一个私有符号标记export const EMPTY_VALUE Symbol(EMPTY_VALUE);见 empty.const.tsget()时的判断逻辑非常清晰getServiceValuecontainer-instance.class.ts#L354-L417如果value不是EMPTY_VALUE直接返回缓存实例否则按factory 优先、类构造兜底的顺序创建然后把实例写回元数据缓存。思想点注册set和实例化get彻底分离。你注册 100 个服务实际只有被get过的那些才会new。这就是为什么注册即创建的写法在大型框架里是性能陷阱而 Tego 选择了惰性。四、设计思想 2三种作用域一张表看懂scope决定了同一个 id能创建出几个实例完整类型见 container-scope.type.ts作用域行为典型用途singleton全局唯一且一定注册在默认容器所有子容器共享同一个实例全局配置、核心管理器container每个容器内各一个实例默认值插件内各用一个独立实例transient每次get都新建不缓存无状态工具类、请求级对象源码里有个精巧的细节container-instance.class.ts#L163-L167在非默认容器里set一个singleton服务时会自动转发到默认容器去注册。这样无论谁注册单例都保证全局唯一用户不需要记规则。另外还有一个eager: true选项注册后立即创建实例transient服务会被忽略因为实例无法缓存管理。适合注册就应该开始工作的服务。五、设计思想 3装饰器把注册变成声明式的对使用者来说最舒服的体验来自三个装饰器入口在 packages/di/src/index.tsService()标记类可注入并自动执行ContainerInstance.default.set(serviceMetadata)。也就是说你只是给类加了个装饰器注册动作在类定义时就完成了见 service.decorator.ts#L14-L33。还支持传参Service({ id: xxx, multiple: true, scope: singleton })Inject()标记一个属性请容器帮我填。它不直接赋值而是向容器登记一个 handler等目标类实例化时再由容器调用container.get()注入见 inject.decorator.ts#L16-L45InjectMany()同 id 注册了多个实现时把整组实例注入为数组inject-many.decorator.ts。5.1 multiple 的实现一个值得学习的技巧multiple: true表面上允许一个 id 对应多个实例但容器内部其实从不直接存一组实例。在set()中container-instance.class.ts#L186-L205为每次注册生成一个内部掩码 Token如MultiMaskToken-xxx把真实服务改挂到这个掩码 Token 下当作普通单实例服务处理原 id 只在multiServiceIds里记录一组掩码 Token。思想点用内部转换把复杂情况归约成简单情况。getMany()只是遍历这组掩码 Token 逐个get而已。核心路径保持单一复杂需求在入口层消化掉。六、设计思想 4多容器 默认容器分层Tego 支持创建多个ContainerInstancenew ContainerInstance(my-id)每个创建都会自动登记到ContainerRegistrycontainer-registry.class.ts#L28-L40保证同一个 id 的容器全局只有一个。查找时的优先级规则get()container-instance.class.ts#L87-L126先看本容器的元数据若默认容器里有singleton同名服务则使用默认容器的那份子容器第一次向父借用服务时先写入一个EMPTY_VALUE占位再创建——这一手是为了防止循环依赖导致无限递归查找是很实用的防御性设计都没有则抛出ServiceNotFoundError。错误处理也很规整找不到服务、无法实例化、类型推断失败分别有专门错误类packages/di/src/error/而不是笼统地throw new Error方便上层精准捕获。七、实战Tego 核心自己怎么用 看真实使用场景比看示例更快。以插件管理器的 Web 服务为例packages/core/src/plugin-manager/web-service.tsService() export class WebControllerService { App() private app: Application; Inject(actions) private actions: MapFunction, any; }三行装饰器就完成了注册服务、注入 Application、按字符串 idactions注入 Action 注册表。而框架侧只需Container.get(WebControllerService)所有依赖自动就绪。这正是插件系统的核心收益——插件代码里找不到任何手动new或全局变量引用。想看更多真实用法推荐直接读测试文件它们就是可运行的文档基础操作与全部作用域用例container-instance.test.ts多实现插件系统场景integration.test.ts#L19-L64装饰器行为decorators.test.ts八、小结5 个设计思想一图流 注册与实例化分离—— 元数据档案 EMPTY_VALUE哨兵惰性创建、按需缓存作用域显式化——singleton/container/transient三档单例自动归拢到默认容器声明式 API——Service定义即注册Inject只声明意图实例化细节全部隐藏复杂问题入口消化——multiple用掩码 Token 归约成单实例逻辑核心路径保持简单防御性细节—— 子容器借用先占位防空循环专门错误类便于排查容器销毁后一切调用立即失败。理解了这个容器再看 Tego 的插件加载、资源路由、Action 系统你会发现它们都遵循同一套面向标识符、由容器装配的思路。建议下一步顺着 packages/core/src/plugin-manager/ 里对Container的调用继续读把插件如何被装配整条链路走通。赞分享低代码后端前端插件系统【免费下载链接】tegoTego 是一个可插拔的 Node.js 框架用于构建可定制的开发平台。它使开发者能够创建自己的无代码/低代码系统或事件驱动型应用程序且框架核心侧重于稳定性与对不同运行环境的适配。项目地址https://gitcode.com/tegojs/tego点击查看免费下载相关推荐RuoYi-Vue-Plus 企业级多租户架构深度解析与设计模式实践RuoYi Vue Plus 企业级多租户架构深度解析与设计模式实践 RuoYi Vue Plus作为Dromara开源社区的企业级Java快速开发平台基于S后端企业应用认证鉴权彻底搞懂Spring IoC从核心容器到依赖注入实战彻底搞懂Spring IoC从核心容器到依赖注入实战 你还在为Spring框架中晦涩的IoCInversion of Control控制反转容器和依赖注后端Web框架依赖注入告别面条代码Express依赖注入(DI)设计模式实战指南告别面条代码Express依赖注入 DI 设计模式实战指南 Express作为一款轻量级Node.js Web框架以其灵活简洁的特性深受开发者喜爱。然而随着后端Web框架创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑