浅析依赖注入框架的生命周期(以 InversifyJS 为例)
在上一篇介绍了 VSCode 的依赖注入设计,并且实现了一个简单的 IOC 框架。但是距离成为一个生产环境可用的框架还差的很远。
行业内已经有许多非常优秀的开源 IOC 框架,它们划分了更为清晰地模块来应对复杂情况下依赖注入运行的正确性。
这里我将以 InversifyJS 为例,分析它的生命周期设计,来弄清楚在一个优秀的 IOC 框架中,完成一次注入流程到底是什么样的。
InversifyJS 的生命周期
在激活 InversifyJS 后,框架通常会监听并经历五个阶段,分别是:
- Annotation 注释阶段
- Planning 规划阶段
- Middleware (optional) 中间件钩子
- Resolution 解析执行阶段
- Activation (optional) 激活钩子
本篇文章将着重介绍其中的三个必选阶段。旨在解释框架到底是如何规划模块实例化的先后顺序,以实现依赖注入能力的。
接下来的解析将围绕如下例子:
@injectable()class FooBar implements FooBarInterface {public foo : FooInterface;public bar : BarInterface;constructor(@inject("FooInterface") foo: FooInterface, @inject("BarInterface") bar: BarInterface) {this.foo = foo;this.bar = bar;}}const container = new Container();const foobar = container.get<FooBarInterface>("FooBarInterface");
Annotation 注释阶段
在此阶段中,框架将通过装饰器为所有接入框架的对象打上标记,以便规划阶段时进行管理。
在这个阶段中,最重要的 API 就是 injectable
。它使用 Reflect metadata,对 Class 构造函数中通过 inject
API 注入的 property 进行标注,并挂在在了该类的 metadataKey
上。
function injectable() {return function(target: any) {if (Reflect.hasOwnMetadata(METADATA_KEY.PARAM_TYPES, target)) {throw new Error(ERRORS_MSGS.DUPLICATED_INJECTABLE_DECORATOR);}const types = Reflect.getMetadata(METADATA_KEY.DESIGN_PARAM_TYPES, target) || [];Reflect.defineMetadata(METADATA_KEY.PARAM_TYPES, types, target);return target;};
}
Planning 规划阶段
本阶段时该框架的核心阶段,它真正生成了在一个 Container 中,所有类模块的依赖关系树。因此,在 Container 类进行实例化时,规划阶段就开始了。
在实例化时,根据传入的 id 与 scope 可以确定该实例容器的作用域范围,生成一个 context,拥有对内左右模块的管理权。
class Context implements interfaces.Context {public id: number;public container: interfaces.Container;public plan: interfaces.Plan;public currentRequest: interfaces.Request;public constructor(container: interfaces.Container) {this.id = id(); // generate a unique idthis.container = container;}public addPlan(plan: interfaces.Plan) {this.plan = plan;}public setCurrentRequest(currentRequest: interfaces.Request) {this.currentRequest = currentRequest;}
}
我们可以注意到,这个 context 中包含一个空的 plan 对象,这是 planning 阶段的核心,该阶段就是为生成的容器规划好要执行的任务。
plan 对象中将包含一个 request 对象,request 是一个可递归的属性结构,它包含了要查找的 id 外,还需要 target 参数,即规定找到依赖实例后将引用赋值给哪个参数。
class Request implements interfaces.Request {public id: number;public serviceIdentifier: interfaces.ServiceIdentifier<any>; // 被修饰类 idpublic parentContext: interfaces.Context;public parentRequest: interfaces.Request | null; // 树形结构的 request,指向父节点public bindings: interfaces.Binding<any>[];public childRequests: interfaces.Request[]; // 树形结构的 request,指向子节点public target: interfaces.Target; // 指向赋值目标参数public requestScope: interfaces.RequestScope;...
}
以篇头的例子为例。在容器执行 get 函数后,框架生成了一个新的 plan,该 plan 的生成过程中将执行_createSubRequests 方法,从上而下创建 Request 依赖树。
创建完成后的 plan 对象生成的 request 树将包含有请求目标为 null 的根 request 与两个子 request:
第一个子 request 指向 FooInterface 接口,并且请求结果的 target 赋值给构造函数中的参数 foo。第二个子 request 指向 BarInterface 接口,并且请求结果的 target 赋值给构造函数中的参数 bar。
注意,此处的依赖树生成仍在 interface 层面,没有任何类被实例化。
用一张图来更直观地表现该阶段中各对象的生成调用过程:
这样,每一个类与其依赖项之间的请求关系就构造完毕了。
Resolution 解析执行阶段
该阶段便是执行在规划阶段中生成的 request 依赖树,从无依赖的叶子节点开始,自下而上实例化每一个依赖类,到根 request 结束时,即最终完成 FooBar
自身的实例化。
且该解析过程可以选择同步或异步执行,在复杂情况下,使用异步懒加载的方式执行解析,有助于提高性能。
至此,一次完整的具有依赖的类的实例化就完成了。我们可以通过打印依赖树,清晰地观察到该实例依赖了哪些实例,从而避免了一切可能的循环依赖,与多次构造依赖带来的内存泄露等很多难以排查的问题。
参考资料
InversifyJS Architecture Overview
相关文章:

浅析依赖注入框架的生命周期(以 InversifyJS 为例)
在上一篇介绍了 VSCode 的依赖注入设计,并且实现了一个简单的 IOC 框架。但是距离成为一个生产环境可用的框架还差的很远。 行业内已经有许多非常优秀的开源 IOC 框架,它们划分了更为清晰地模块来应对复杂情况下依赖注入运行的正确性。 这里我将以 Inv…...

HER2靶向药物研发进展-销售数据-上市药品前景分析
HER2长期作为肿瘤领域的热门靶点之一,其原因是它在多部位、多种形式的癌症中均有异常的表达,据研究表明HER2除了在胃癌、胆道癌、胆管癌、乳腺癌、卵巢癌、结肠癌、膀胱癌、肺癌、子宫颈癌、子宫浆液性子宫内膜癌、头颈癌、食道癌中的异常表达还存在于多…...

【第38天】不同路径数问题 | 网格 dp 入门
本文已收录于专栏🌸《Java入门一百例》🌸学习指引序、专栏前言一、网格模型二、【例题1】1、题目描述2、解题思路3、模板代码4、代码解析5.原题链接三、【例题2】1、题目描述2、解题思路3、模板代码4、代码解析5.原题链接三、推荐专栏四、课后习题序、专…...

LINUX之链接命令
链接命令学习目标能够说出软链接的创建方式能够说出硬链接的创建方式1. 链接命令的介绍链接命令是创建链接文件,链接文件分为:软链接硬链接命令说明ln -s创建软链接ln创建硬链接2. 软链接类似于Windows下的快捷方式,当一个源文件的目录层级比较深&#x…...

1628_MIT 6.828 xv6_chapter0操作系统接口
全部学习汇总: GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 这本书最初看名字以为是对早期unix的一个解读,但是看了开篇发现 不完全是,只是针对JOS教学OS系统来做的一些讲解。 Xv6是对UNIX v6的重新实…...

使用 Sahi 实现 Web 自动化测试
Sahi 是 Tyto Software 旗下的一个基于业务的开源 Web 应用自动化测试工具。Sahi 运行为一个代理服务器,并通过注入 JavaScript 来访问 Web 页面中的元素。Sahi 支持 HTTPS 并且独立于 Web 站点,简单小巧却功能强大。它相对于 Selenium 等自动化测试工具…...

天津菲图尼克科技携洁净及无菌防护服解决方案与您相约2023生物发酵展
BIO CHINA 生物发酵产业一年一度行业盛会,由中国生物发酵产业协会主办,上海信世展览服务有限公司承办,2023第10届国际生物发酵产品与技术装备展览会(济南)于2023年3月30-4月1日在山东国际会展中心(济南市槐…...

Java 网络编程详解
1、什么是网络编程 在网络通信协议下,不同计算机上运行的程序,可以进行数据传输。 应用场景: 1、即时通信 2、网游对战 3、邮件等等 Java中可以使用java.net包下的技术轻松开发出常见的网络应用程序 2、网络编程三要素 2.1 IP地址 要…...
Scratch少儿编程案例-几何形式贪吃蛇
专栏分享 点击跳转=>Unity3D特效百例点击跳转=>案例项目实战源码点击跳转=>游戏脚本-辅助自动化点击跳转=>Android控件全解手册点击跳转=>Scratch编程案例👉关于作者...

一定要收藏的面试思维导图,粉丝分享面试经验
一位粉丝分享面试经验:1.常见面试题有哪些?主要从以下一些知识点做了准备: 常用的分析方法、Excel、SQL、 A/B测试、产品分析。然后每份面试针对职位要求,还有前期和HR聊天一点点了解这个职位之后,定向准备。 Excel、S…...

【博客615】通过systemd设置cgroup来限制服务资源争抢
通过systemd设置cgroup来限制服务资源争抢 1、场景 我们的宿主机上通常会用systemctl来管理一些agent服务,此时我们需要限制服务的cpu,memory等资源用量,以防止服务之前互相争抢资源,导致某些核心agent运行异常 2、systemd与cgro…...

C语言经典编程题100例(21-40)
21、练习3-2 计算符号函数的值对于任一整数n,符号函数sign(n)的定义如下:请编写程序计算该函数对任一输入整数的值。输入格式:输入在一行中给出整数n。输出格式:在一行中按照格式“sign(n) 函数值”输出该整数n对应的函数值。输入样例1:10输出样例1:sig…...

Rabbitmq业务难点
Rabbitmq业务难点1.消息生产者发送的消息无法路由到任何一个队列怎么处理?2.聊聊Rabbitmq的七种工作模式3.Rabbitmq的消息确认机制4.Rabbitmq的消息持久化5.发布确认模式如何确保生产者能够成功将消息投递到消息队列6. Rabbitmq基于队列设置消息过期时间和单独针对消息设置过期…...

服务器如何下载百度网盘文件?Linux服务器如何在百度网盘中连接、上传下载;在Linux服务器上下载百度云盘中的资料
前言 百度云提供Python包bypy进行远程服务器的对接然后下载: https://github.com/houtianze/bypy 可以通过pip直接下载,授权本人的百度云账号后,就可以直接使Linux电脑本地文件与百度网盘的apps(我的应用数据)/bypy目…...

Cesium-数字仿真-你总要了解
Cesium(专注于时空数据的实时可视化) cesium是一款三维地球开源框架(可以多平台、跨平台使用)cesium隶属于美国AGI公司(Analytical Graphics Incorporation),美国通用公司宇航部的工程师创始开源 周边产…...

原型、原型链、__proto__与prototype的区别、继承
一.什么是原型与原型链 根据MDN官方解释: JavaScript 常被描述为一种基于原型的语言——每个对象拥有一个原型对象[[Prototype]] ,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类…...
前端 面经
1说一说cookie sessionStorage localStorage 区别?解题思路得分点 数据存储位置、生命周期、存储大小、写入方式、数据共享、发送请求时是否携带、应用场景 标准回答 Cookie、SessionStorage、 LocalStorage都是浏览器的本地存储。 它们的共同点:都是存储…...

[oeasy]python0080_设置RGB颜色_24bit_24位真彩色_颜色设置
RGB颜色 回忆上次内容 上次 首先了解了 索引颜色 \33[38;5;XXXm 设置 前景为索引色\33[48;5;XXXm 设置 背景为索引色 RGB每种颜色 可选0-5总共 6 级 想用 精确RGB值 真实地 大红色画个 大红桃心 ♥️ 有可能吗??🤔 rgb 模式 关于 RGB 模式…...

实战项目-用户评论数据情绪分析
目录1、基于词典的方法2、基于词袋或 Word2Vec 的方法2.1 词袋模型2.2 Word2Vec3、案例:用户评论情绪分析3.1 数据读取3.2 语料库分词处理3.3 Word2Vec 处理3.4 训练情绪分类模型3.5 对评论数据进行情绪判断目的:去判断一段文本、评论的情绪偏向在这里&a…...
day02 DOS(续)文本编辑快捷键 发展史
day02课堂笔记 1、常用的DOS命令(续) 1.1、del命令,删除一个或者多个文件 删除T1.class文件 C:\Users\Administrator>del T1.class 删除所有.class结尾的文件,支持模糊匹配 C:\Users\Administrator>del *.class T1.classT1…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
土建施工员考试:建筑施工技术重点知识有哪些?
《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目,核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容,附学习方向和应试技巧: 一、施工组织与进度管理 核心目标: 规…...