objbox
v1.7.15
Published
基于typescript装饰器的轻量级IOC容器
Maintainers
Readme
可以从npmjs.org 或 npmmirror.com 使用"npm install objbox"获取
github or npmjs 每个中版本的最后一个版本都是正式版,其余为测试版,例如有如下版本 1.2.1、1.2.2、1.2.3、1.3.1、1.3.2 ,其中只有1.2.3是正式版 小版本号表示bug修复,中版本表示有重要特性增加
简述
为了减少typescript项目中逻辑与业务的依赖与高耦合,以及可以更方便对对象进行管理,故编写objbox(对象盒子)IOC容器。objbox是轻量级的,它支持文件扫描、class注册,对象注册、生命周期管理,自定义注解处理等。
objbox是基于typescript的装饰器的,你的项目需要支持typescript。当然,不是必须使用装饰器,只是推荐使用装饰器,且可以完成更多更强的功能
typescript配置
typescript配置:
{ "include": [ ], "compilerOptions": { "experimentalDecorators": true, //开启装饰器【必须】 "module": "commonjs", // commonjs 模式【必须】 "target": "ES6", // es6标准【至少满足es6】 "strict": false, // 关闭严格模式【推荐】,不然会出现一些编译上的繁琐问题 "outDir": "./out", "paths": {}, "types": [ "node" ] } }
基础样例
import { ComponentCreatedType, ObjBoxHelper } from "objbox";
import { Level, LoggerManagerConfig, TimeFlag } from "objbox/libs";
import { DefaultApplicationHandler } from "objbox/demo/HandlerDemo/DefaultApplicationHandler"
import { DefaultComponentHandler } from "objbox/demo/HandlerDemo/DefaultComponentHandler"
function main() {
// 配置容器日志(非必要)
let loggerConfig: LoggerManagerConfig = {
level: Level.ALL,
timeFormate: `${TimeFlag.Year}-${TimeFlag.Month}-${TimeFlag.Day} ${TimeFlag.Hour}:${TimeFlag.Minute}:${TimeFlag.Second}`
}
let ob = ObjBoxHelper.newObjBox(loggerConfig);
// 注册处理器(非必要)
ob.registerFromClass(DefaultApplicationHandler)
ob.registerFromClass(DefaultComponentHandler)
// 方式1:从文件扫描与注册模板
// ob.registerFromFiles([
// new ScanDir(__dirname + "/src")
// ])
//方式2:通过method注册
ob.registerFromMethod(() => {
return { a: 123 }
}, "AA", ComponentCreatedType.Singleton)
// 方式3:通过class注册
class A {
v: 123
}
ob.registerFromClass(A);
//方式4:直接注册对象
ob.registerByObject({ v: 123 }, "obj")
// 启动装载
ob.load()
//启动容器应用
ob.run();
//获取注册的组件
console.log(ob.getComponent("AA"))
console.log(ob.getComponent("A"))
console.log(ob.getComponent("obj"))
/**
* 更方便的基于文件扫描的注解方式请查看readme
*/
}
main()流程说明
/**
* 基于typescript与nodejs的轻量级IOC容器
*
* ObjBox思想就是,一切皆组件(Component)
* 一切围绕着如何创建组件、如何管理组件、如何执行组件三个方面进行
* 思路是通过扫描(注册)class、method、Object构建模板template,将信息存储到模板上(包括创建的组件信息、创建方式、来源等)、
* 随后在load阶段对模板进行模板遍历与生成组件,并触发对应的处理器与生命周期钩子
*
* ObjBox内有3种基本概念:组件、模板、注解
* 模板:指的是具有 @Component 注解标记且export的class
* 组件:被实例化的模板
* 注解:也就是 @xxx 叫做注解(支持class注解、method注解,property注解、methodArguments注解,注解信息存储在模板的prototype内,作为模板的元数据)
* 他们的处理顺序为注解、模板、组件
* 基础注解有:
@ApplicationHandler 应用处理器
@ComponentHandler 组件处理器
@BeanComponent 创建Bean组件的组件(bean组件工厂)
@Bean 通过method创建组件
@Component 组件注解
@AutowireProperty 通过属性注入
@AutowireMethod 通过方法注入
*/
/**
以文件注册为流程简述IOC处理流程
================== 预处理阶段 ==================
说明:预处理阶段是利用typescript的特性以及装饰器特性,进行元数据注入
0、typescript编译以及被特定装饰器预处理
================== 注册阶段 ==================
说明:注册阶段可以向容器内注册模板,可以从文件、class、method等方式进行注册(或者多种方式并用也可以,只要在),这里以文件方式举例
1、扫描组件文件,生成class的function
2、验证function的prototype规范性
3、通过function创建组件扫描模板ScannedTemplate
4、校验模板是否为ApplicationHandler实例化并存储
5、存储所有组件模板(如果ApplicationHandler被标注为Component,会成为单例组件)
================== 装载阶段 ==================
===== 装载前期 =====
说明:实际上在装载中期之前,可以通过ApplicationHandler的start进行模板注册。不允许在注册阶段之后注册任何ApplicationHandler,会不断触发start导致死循环
6、对所有模板触发 @ApplicationHandler 的 start(objBox)
===== 装载中期 =====
7、对所有模板触发 @ApplicationHandler 的 preprocessScannedTemplate(objbox,sTemplates[])
8、校验模板是否为 @ComponentHandler 实例化并存储
9、校验模板是否为 @BeanComponent 实例化并创建 @Bean 的组件模板
10、对新建的 @bean 模板触发 @ApplicationHandler 的 preprocessScannedTemplate(objbox,sTemplates[])
11、对所有模板触发 @ComponentHandler 的 scaned(objbox,name,sTemplate)
===== 装载后期 =====
12、触发应用处理器 @ApplicationHandler 的 processBeforePrepare(objbox)
13、对所有模板创建第一个实例组件并进行依赖注入
13.1、从模板单例实例化处获取实例,如果没有去缓存取
13.2、如果缓存没有,新建
13.3、新建 @Component 组件 ObjBox.createComponentFromTemplate(sTemplate)
13.4、触发 @ComponentHandler 的 beforeCreated(objbox,sTemplate,component)
13.5、触发 @TemplateHandler 的 created
13.6、触发 @ComponentHandler 的 afterCreated(objbox,sTemplate,component)
13.7、依赖注入 @Component 组件 objbox.injectComponentDependency(component)
13.8、触发 @ComponentHandler 的 beforeCompleted(objbox,sTemplate,component)
13.9、触发 @TemplateHandler 的 completed
13.10、触发 @ComponentHandler 的 afterCompleted(objbox,sTemplate,component)
13.11、如果应用已经运行
13.11.1、触发 @ComponentHandler 的 beforeReady
13.11.2、触发 @TemplateHandler 的 ready
13.11.3、触发 @ComponentHandler 的 afterReady
14、触发应用处理器 @ApplicationHandler 的 processAfterPrepare(objbox)
================== 运行阶段 ==================
说明:容器正式启动,触发所有组件的ready接口
15、run启动程序
15.1、触发 @ApplicationHandler 的 beforeRunning(objbox)
15.2、触发 @ComponentHandler 的 beforeReady(objbox,sTemplate,component)
15.3、触发 @TemplateHandler 的 ready
15.4、触发 @ComponentHandler 的 afterReady(objbox,sTemplate,component)
15.5、触发 @ApplicationHandler 的 afterRunning(objbox)
================== 卸载阶段 ==================
说明:手动触发unload卸载程序,解除程序所有的模板与实例化的全部引用,将程序清空
16、unload卸载程序
16.1、触发 @ApplicationHandler 的 beforeUnload(objbox)
16.2、触发 @ComponentHandler 的 beforeUnloaded(objbox,sTemplate,component)
16.3、触发 @TemplateHandler 的 unloaded
16.4、触发 @ComponentHandler 的 afterUnloaded(objbox,sTemplate,component)
16.5、触发 @ApplicationHandler 的 afterUnload(objbox)
*/流程图
graph TD
A[开始] --> B[进入预处理阶段]
%% 预处理阶段
B -->|0.TypeScript编译及装饰器预处理| C[注入元数据到_annotations_]
%% 注册阶段
C --> D[进入注册阶段]
D -->|1.注册模板| E{注册方式?}
E -->|文件| F[方法:registerFromFiles]
E -->|类| G[方法:registerFromClass]
E -->|方法| H[方法:registerFromMethod]
E -->|对象| I[方法:registerByObject]
F --> J{是ApplicationHandler?}
G --> J
J -->|是| K[方法:trySaveApplicationHandler]
J -->|否| L[方法:trySaveComponentTemplate]
K --> L
%% 装载阶段
L --> M[进入装载阶段]
H --> M
I --> M
M --> N[装载前期]
N -->|2.ApplicationHandlerInterface.start| O[触发:executeApplicationHandler_start]
O --> P[装载中期]
P -->|3.ApplicationHandlerInterface.preprocessScannedTemplate| Q[触发:executeApplicationHandler_preprocessScannedTemplate]
Q -->|4.ComponentHandlerInterface.scanned| R{是ComponentHandler?}
R -->|是| S[方法:trySaveComponentHandler]
R -->|否| T{是 BeanComponent?}
T -->|是| U[方法:trySaveBeanComponent<br>createBeanTemplatesFromBeanComponent]
U --> Empty01[ ]
S --> Empty01
T --> Empty01
Empty01 -->|5.ApplicationHandlerInterface.preprocessScannedTemplate| W[触发<br>executeApplicationHandler_preprocessScannedTemplate<br>处理Bean模板]
W -->|6.ComponentHandlerInterface.scanned| X[触发:executeComponentHandler_scanned]
X --> Y[装载后期]
Y -->|7.ApplicationHandlerInterface.beforePrepare| Z[触发:executeApplicationHandler_BeforePrepare]
Z -->|8.创建组件及依赖注入| AA{模板已加载?}
AA -->|否| AB[方法:prepareComponents_WhenLoading]
AA -->|是| AC[跳过创建]
AB -->|9.ComponentHandlerInterface.beforeCreated| AD[触发:executeComponentHandler_beforeCreated]
AD -->|10.TemplateHandlerInterface.created| AE[触发:executeTemplateHandler_created]
AE -->|11.ComponentHandlerInterface.afterCreated| AF[触发:executeComponentHandler_afterCreated]
AF -->|12.依赖注入| AG[方法:injectComponentDependency]
AG -->|13.ComponentHandlerInterface.beforeCompleted| AH[触发:executeComponentHandler_beforeCompleted]
AH -->|14.TemplateHandlerInterface.completed| AI[触发:executeTemplateHandler_completed]
AI -->|15.ComponentHandlerInterface.afterCompleted| AJ[触发:executeComponentHandler_afterCompleted]
AJ -->|16.条件触发| AK{应用已运行?}
AK -->|是| AL[ComponentHandlerInterface.beforeReady<br>触发:executeComponentHandler_beforeReady]
AL -->|17.TemplateHandlerInterface.ready| AM[触发:executeTemplateHandler_ready]
AM -->|18.ComponentHandlerInterface.afterReady| AN[触发:executeComponentHandler_afterReady]
AK -->|否| AO[跳过ready钩子]
AN --> AP[19.ApplicationHandlerInterface.afterPrepare]
AC --> AP
AO --> AP
AP -->|触发:executeApplicationHandler_AfterPrepare| AQ[结束装载]
%% 运行阶段
AQ --> AR[进入运行阶段]
AR -->|20.ApplicationHandlerInterface.beforeRunning| AS[触发:executeApplicationHandler_beforeRunning]
AS -->|21.ComponentHandlerInterface.beforeReady| AT[触发:executeComponentHandler_beforeReady]
AT -->|22.TemplateHandlerInterface.ready| AU[触发:executeTemplateHandler_ready]
AU -->|23.ComponentHandlerInterface.afterReady| AV[触发:executeComponentHandler_afterReady]
AV -->|24.ApplicationHandlerInterface.afterRunning| AW[触发:executeApplicationHandler_afterRunning]
AW --> AX[结束]
%% 样式
classDef stage fill:#f9f,stroke:#333,stroke-width:2px;
class B,D,M,AR stage;
classDef phase fill:#bbf,stroke:#333,stroke-width:1px;
class N,P,Y phase;注解方式组件
注解方式可以通过文件扫描,也可以通过import导入(推荐使用扫描,毕竟IOC容器目的就是减少依赖)
//main.ts
import { ObjBoxHelper, ScanDir } from "objbox";
import { Level, LoggerManagerConfig, TimeFlag } from "objbox/libs";
import { DefaultApplicationHandler } from "objbox/demo/HandlerDemo/DefaultApplicationHandler"
import { DefaultComponentHandler } from "objbox/demo/HandlerDemo/DefaultComponentHandler"
import * as fs_extra from 'fs-extra';
function main() {
// 配置容器日志(非必要)
let loggerConfig: LoggerManagerConfig = {
level: Level.ALL,
timeFormate: `${TimeFlag.Year}-${TimeFlag.Month}-${TimeFlag.Day} ${TimeFlag.Hour}:${TimeFlag.Minute}:${TimeFlag.Second}`
}
let ob = ObjBoxHelper.newObjBox(loggerConfig,fs_extra);
// 注册处理器(非必要)
ob.registerFromClass(DefaultApplicationHandler)
ob.registerFromClass(DefaultComponentHandler)
// 方式1:从文件扫描与注册模板
ob.registerFromFiles([
new ScanDir(__dirname + "/src")
])
// 启动装载
ob.load()
//启动容器应用
ob.run();
}
main()然后新建对应的目录src,目录结构如下
+--------
+-main.js
+-src
+---------
+-xxx.ts
+...并且在内部编写如下代码文件
//xxx.ts
import { AutowireMethod, AutowireProperty, Bean, BeanComponent, Component, ComponentCreatedType,TemplateHandler } from 'objbox';
// @Component()
// export class DefaultComponent implements TemplateHandler{
// created(){
// console.log("DefaultComponent-created")
// };
// completed(){
// console.log("DefaultComponent-completed")
// };
// ready(){
// console.log("DefaultComponent-ready")
// };
// }
@Component("A")
export class A{
}
@Component()
export class B{
}
@BeanComponent()
export class MyBeanComponent{
@Bean("C",ComponentCreatedType.Singleton)
createC(){
return {
msg:"this is C"
}
}
}
@Component()
export class Main implements TemplateHandler{
@AutowireProperty("A")
a:A
b:B
@AutowireMethod("B")
setB(b:B){
this.b = b;
}
@AutowireProperty("C")
c:any
ready(){
// console.log(this.a,this.b,this.c)
}
}
@Component()
export class R1{
@AutowireProperty("R2",false)
r2:R2
}
@Component("R2")
export class R2{
@AutowireProperty("R1")
r1:R1
}编译main.ts以及xxx.ts之后执行main.js即可。推荐如下配置tsconfig.json,将扫描的路径包含编译进去
{
...
"include": [
"main.ts",
"./src/**/*"
],
...
}创建自定义注解
通过全局安装npm install -g objbox,还可以使用创建注解命令
objbox anno-class <name> <path> //create an annotation of class in path
objbox anno-property <name> <path> //create an annotation of property in path
objbox anno-method <name> <path> //create an annotation of method in path
objbox anno-methodArg <name> <path> //create an annotation of methodArg in path使用注解
通过阅读objbox流程,可以在ApplicationHandler或ComponentHandler中使用ObjBoxHelper进行操作注解,以及操作原始数据,达到自定义注解效果
//main.ts
import { Component, ComponentHandler, ComponentHandlerInterface, ObjBoxHelper, ObjBoxInterface, ScanDir, ScannedTemplate, getFunName, registerMethod } from "objbox";
import { LoggerManagerConfig, TimeFlag, Level } from "objbox/libs";
/**
* 默认方法注解模板
* @param yourArg1
* @param yourArg2
*/
export function Log(): MethodDecorator {
//获取当前函数名称,等效于let _annotationName = "Log"
let _annotationName = getFunName(2)
//@ts-ignore
return function (target: any, key: string, descriptor: PropertyDescriptor) {
registerMethod<any>(_annotationName, {}, target, key, descriptor)
}
}
@ComponentHandler()
class LogHandler implements ComponentHandlerInterface {
scanned: (objbox: ObjBoxInterface, template: ScannedTemplate) => void;
beforeCreated(objbox: ObjBoxInterface, template: ScannedTemplate, component: any) {
let methodAnnos = ObjBoxHelper.getMethodsAnnotationFromComponent(Log.name, component)
for (let methodAnno of methodAnnos) {
ObjBoxHelper.insertFunctionBeforeMethod(component, methodAnno.methodName, (...args) => {
console.log("args: ", ...args);
return args
})
ObjBoxHelper.insertFunctionAfterMethod(component, methodAnno.methodName, (result: any) => {
console.log("result: " + result)
return result
})
}
}
beforeCompleted?: (objbox: ObjBoxInterface, template: ScannedTemplate, component: any) => void;
beforeReady?: (objbox: ObjBoxInterface, template: ScannedTemplate, component: any) => void;
}
@Component()
class YourClass {
@Log()
add(num1: number, num2: number) {
return num1 + num2
}
}
function main() {
// 配置容器日志(非必要)
let loggerConfig: LoggerManagerConfig = {
level: Level.ALL,
timeFormate: `${TimeFlag.Year}-${TimeFlag.Month}-${TimeFlag.Day} ${TimeFlag.Hour}:${TimeFlag.Minute}:${TimeFlag.Second}`
}
let ob = ObjBoxHelper.newObjBox(loggerConfig);
ob.registerFromClass(LogHandler)
ob.registerFromClass(YourClass)
// 启动装载
ob.load()
//启动容器应用
ob.run();
let yourclass: YourClass = ob.getComponent(YourClass.name)
yourclass.add(123, 456);
}
main()