加载机制 Loader
从概念上看,Loader 更加侧重的是以编程约束规范来提升项目协作一致性体验,因此整体设计上不与请求模型中的 Context/Trigger/Middleware 做过深耦合。
Core 内置 Loader 实现,主要的环节包括:
- FindPath 扫描目录/读取 Manifest
- Require 加载模块并 attach 对应的 Meta
- Mount 将对象池交付并按照其上的 Meta 挂载到 Context/Application or IOC Contianer
其中,Manifest 用于描述目录、Path、MetaHandler 的关系,格式如下:
目录扫描/读取 Manifest
Core 中内置默认的目录扫描规范,优先级 CustomLoader -> Manifest -> 目录。
基于目录
- 以下路径属于内置于 Core 的加载部分
- config:
${rootDir}/config/config.${env}.ts - plugin:
${rootDir}/config/plugin.${env}.ts - extend:
${rootDir}/app/extend/application.ts${rootDir}/app/extend/context.ts${rootDir}/app/extend/request.ts${rootDir}/app/extend/helper.ts
- config:
- 上层框架的协议实现可以补充加载路径,如:
- service:
${rootDir}/service/${name}.ts - controller:
${rootDir}/controller/${name}.ts - router:
${rootDir}/router/${name}.ts
- service:
基于Manifest
mainfest.json 文件提供扁平化的文件路径,提供给 loader 加载。
以 HTTP 框架中的常见 Case 为例:
iTerm
CustomLoader
iTerm
配置加载 Load Config
对于配置文件 config.${env}.ts 的加载,将根据服务的环境变量做如下处理。
- 默认约定配置文件为
config.default.ts,默认环境变量ARTUS_SERVER_ENV,可以通过 hook 来定制相关的环境变量解析方式。 - 根据环境变量
env来加载对应config.${env}.ts配置文件 config.${env}.ts将采用 deepmerge 的方式进行合并,值得注意的是对于数组项的合并,框架采用了直接替换的方式,因为数组元素之间往往存在顺序的要求,直接合并可能会导致与预期不符的结果。例如 环境变量为ARTUS_SERVER_ENV=prod,config.prod.ts将会覆盖并合并config.default.ts- config 支持异步,可通过异步函数来实现更加复杂的配置。由于 Artus 默认采用单进程模型,需要增强远程配置获取的前置能力,以便放置类似的异步配置加载逻辑。常见的异步获取远程配置有密钥管理服务 KMS(Key Management Service), 通过密钥来保护后台应用配置文件。
- 配置加载完毕后,需要 dump 出配置信息用于分析和排查
- [TODO] 可以通过 setConfig API,在 config 加载完成后对 config 进行修改, 以便框架记录配置改动的操作位置,方便分析
iTerm
iTerm
ARTUS_SERVER_ENV 默认值枚举值如下
iTerm
default: 默认值,即没有环境变量时默认读取的配置文件名config.default.tsdevelopment: 本地开发环境,即ARTUS_SERVER_ENV=development时,将config.development.ts和config.default.ts进行 deepmerge 操作production: 某种意义上的生产环境,即ARTUS_SERVER_ENV=production时,将config.production.ts和config.default.ts进行 deepmerge 操作
模块/文件加载 Load Module / File
通过上一环节的目录扫描与读取,Loader 将能够持有一份标准化的入口文件 Path 列表(统称为 Manifest),Loader 需要针对该部分文件进行加载行为:
- 对于 JS / TS 文件,以模块的形式
- 其中,模块应包括 Meta 信息用于最终消费环节(如:Scope),有以下形式可用:
- JS 编程界面中通过预先配置的形式由上层框架指定
- TS 编程界面使用上层框架实现对应的装饰器,调用 Core 提供的接口使用 Reflect attach 上述信息到 Class
- 模块的加载应当兼容 CommonJS 和 ECMAScript Module
- 其中,模块应包括 Meta 信息用于最终消费环节(如:Scope),有以下形式可用:
- 对于 JSON 等元信息文件,利用 Manifest 或 Loader 配置中的解析规则处理
POC 如下:
- 加载流程
iTerm
- Meta 信息挂载(JS)
- 按照目录约定的形式,Meta 由上层框架通过对 Loader 的预先配置指定
- Meta 信息挂载(TypeScript Decorator)
iTerm
基于 IoC 的挂载机制
- Artus 中的 Core 实现底层基于 IoC(控制反转,Inversion of Control)实现,便于在上层框架中集成 DI(依赖注入,Dependency Injection)、AOP(面向切面编程,Aspect Oriented Programming)等特性
- 实现原理为:
- 在 Core 中按照 Scope 提供 IOC Container 作为基础对象池,Scope 包括
- Singleton(单例生命周期,全局执行一次实例化)
- Execution(执行生命周期,每次执行时实例化)
- Transient(临时生命周期,每次使用时实例化)
- Loader 及上层框架的 Cutsom Loader 应当在挂载环节中将加载的类对象
- 把 Controller 和 Service 等类的实例化创建过程托管给容器
- Config 配置内容托管给容器
- 插件中扩展的属性等也托管给容器,用户能够在 IoC 模式下注入这些扩展的属性
- 例如 Redis、MySQL Client
- 希望兼容现有 Egg/Gulu 中 app/ctx 等数据模型的前提下,提供 IoC 的开发模式
- 对于上层框架,暴露 IoC Container 的操作方法,用于实现 DI/AOP...
- 对于既有应用迁移,提供 delegate 代理层,模拟传统 app/ctx 用法,代理层的 getter/setter 访问 IoC Container
- 在 Core 中按照 Scope 提供 IOC Container 作为基础对象池,Scope 包括
PoC 如下
- Container 容器伪代码
iTerm

lib/loader/mixin/controller.js


- 以注入形式使用 IoC 托管的类实例(TypeScript)
iTerm
- 以传统模型使用 IoC 托管的类实例(JavaScript/代理层)
- 注:如有异步情况(getAsync)这种代理层可能不能完全向下兼容(需要 await)
- 对于代理层不能完全处理的情况(如:异步)需要在分析(构建时)或启动(运行时)给出 warning 提示
iTerm