摘要:項目地址項目主頁基于和注解的輕量級容器,提供了依賴注入面向切面編程及異常處理等功能。可在任意工程中引入,是一個框架無關的容器。模塊不依賴于任何框架,并與現有框架庫類等保持兼容。
Rockerjs Core
項目地址
項目主頁
基于 TypeScript 和注解的輕量級IoC容器,提供了依賴注入、面向切面編程及異常處理等功能。Rockerjs Core可在任意工程中引入,是一個框架無關的IoC容器。
@rockerjs/core模塊不依賴于任何框架,并與現有框架、庫、類等保持兼容。通過DI(Dependency Injection)實現代碼解耦和依賴解耦,在構建復雜應用時保證可擴展性與靈活性;同時提供二維編程的能力,基于注解可在各個連接點(Advice)進行非核心業務的操作,減少代碼冗余;最后,它提供一種基于注解配置的簡易異常處理機制 -- Clamp機制,通過特定規則匹配異常處理程序實現處理。
一、快速上手安裝
npm install @rockerjs/core
@rockerjs/core最佳實踐需要結合TypeScript的裝飾器一起使用(也可使用接口),因此需要在項目根目錄添加 tsconfig.json 文件,并配置編譯選項 “experimentalDecorators”和“emitDecoratorMetadata”為 true
示例 1
import { Container, Inject } from "@rockerjs/core"; class User { id: string = "testId"; name: string = "testName"; } class UserService { getUser(_id: string): User { return new User(); } } @Inject class ControlDefault { @Inject userService: UserService; test() { let user: User = this.userService.getUser("test"); console.log(user); } } @Inject("controllor-with-args", new Date()) class ControlDefaultWithArgs { name: string; time: Date; constructor(name: string, time: Date) { this.name = name; this.time = time; } @Inject userService: UserService; test() { let user: User = this.userService.getUser("test"); console.log(user, this.name, this.time); } } @Inject("controllor1", "util", new Date()) class Control { name: string; time: Date; constructor(name: string, time: Date) { this.name = name; this.time = time; } @Inject userService: UserService; test() { let user: User = this.userService.getUser("test"); console.log(user, this.name, this.time); } } // 通過getObject接口從容器中獲取實例,參數為“單例的名稱”(默認名稱為類名首字母小寫) Container.getObject("controlDefault").test(); // 通過getObject接口從容器中獲取實例,此例中并未提供實例名 Container.getObject ("controlDefaultWithArgs").test(); // 通過getObject接口從容器中獲取實例,此例中提供了3個參數,@rockerjs/core 認為第一個參數為實例名,剩下的參數則用于實例化 Container.getObject ("controllor1").test();
示例 2 : RPC
import {Container, Inject} from "@rockerjs/core"; //PRC Demo實現 let RPC = { config: function (cfg: { serviceUrl: string, interfaces: Function[] }) { if (cfg.interfaces) { cfg.interfaces.forEach((type: FunctionConstructor) => { if (type.prototype) { let newObj = {}, proto = type.prototype; let nms = Object.getOwnPropertyNames(proto); if (nms) { nms.forEach((nm) => { if (nm != "constructor" && typeof(proto[nm]) === "function") { newObj[nm] = function () { //{nm:方法名,arguments:參數表},改為調用遠程請求過程 return arguments[0];//test return } } }) } Container.provides([type, () => { return newObj; }]) } }) } } } //--DEMO-------------------------------------------------------- //1. 接口聲明(注意,此處只能使用Concrete class) class Product { getById(id: string): string { return null; } } //2. 應用RPC Framework RPC.config({ serviceUrl: null, interfaces: [Product]//提供接口描述,在RPC中構建factory }) //3. Service class @Inject class Service { @Inject product: Product; test() { let id: string = "tid"; let rst = this.product.getById(id); console.log(rst); } } //4.測試 Container.getObject二、依賴注入與容器 依賴注入 @Inject("service").test();
提供了注解 @Inject 來實現依賴的注入,當我們有如下 GetDubboData 類時
class GetDubboData { p0: number; constructor(p0: number, p1: string) { this.p0 = p0; } }
我們可以通過以下方式實例化這個類,同時傳入指定的參數
直接傳遞構造函數的參數
class SomeControl { @Inject(1, "aaa") private dubbo: GetDubboData }
給出構造函數的工廠函數
class SomeControl { @Inject(function () { return [1, "aaa"] }) private dubbo: GetDubboData }
無構造函數或參數為空
class SomeControl { @Inject private dubbo: GetDubboData }操作類實例化容器
默認的實例化方法可以滿足開發者的大部分需求,Rockerjs Core 提供了 provides 方法自定義實例化工廠,同時提供了獲取類和類實例化函數映射表的方法。
注冊、修改類的實例化方法
直接傳入類或工廠函數
// 形式一:形如 Container.provides(class extends UserService{}) Container.provides( class extends UserService { getUser(id: string): User { console.log(1); return new User(); } } );
傳入類及類的工廠函數
// 形式二:形如 Container.provides([UserService,FactoryFunction]) Container.provides([ UserService, () => { return new class extends UserService { getUser(id: string): User { console.log(2); return new User(); } }(); } ]);獲取實例化方法注冊表
返回一個構造函數-工廠方法映射表, 結構如下
const globalGeneralProviders: Map手動實例化方法= new Map< FunctionConstructor, Function >();
Container.injectClazzManually 方法提供了直接實例化注冊表中的類的功能,參數為構造函數以及想要傳入的參數
class SomeControl { transGet: GetTransData = Container.injectClazzManually(GetTransData, 1, 2); async getProduct(_productId?: number) { let json: any = await this.transGet.getDetail(_productId); console.log(json); } }完整例子
假設我們有一個獲取異步數據的抽象類
abstract class GetTransData { p0: number constructor(p0: number, p1: string) { console.log(p0 + p1) this.p0 = p0 } abstract async getDetail(_proId: number): Promise; }
可以通過 Container 的 provides API 來指定對應類型的工廠函數
Container.provides([GetTransData, (_p0, _p1) => { return new class extends GetTransData { constructor(p0: number, p1: string) { super(p0, p1); } async getDetail(_id: number): Promise{ await ((ms) => new Promise(res => setTimeout(res, ms)))(100) return `Hello ${this.p0}` } }(_p0, _p1); }]);
最終通過 @Inject 方法注入在測試類里面實例化這個對象
@Inject class SomeControl { @Inject(666, 2) transGet: GetTransData; async getProduct(_productId?: number) { let json: any = await this.transGet.getDetail(_productId); console.log(json); } } Container.getObject("someControl").getProduct();
得到輸出結果
668 Hello 666三、面向切面編程 AOP
面向切面編程(AOP是Aspect Oriented Program的首字母縮寫)是指在運行時,動態地將代碼切入到類的指定方法、指定位置上的編程思想。Rockerjs Core 提供了 AOP 編程能力
簡單的例子假如我們想在下面的 foo 方法執行前后打點
class Test { foo() { console.log("foo") } } new Test().foo()
我們可以聲明一個日志類,通過@Aspect注解聲明其為一個切面類,通過@Pointcut注解配置想要匹配的類、方法以及需要執行的鉤子, 最后通過 @Before和@After等注解標識被修飾方法在處于對應生命周期時需要執行的方法
import { Aspect, Pointcut, Before, After } from "@rockerjs/core"; @Aspect class Logger { // 必須在靜態方法上注冊切點 @Pointcut({ clazz: Test, // 定位被修飾的類 rules: ".*foo.*", // 通過正則匹配到對應的方法,不填則匹配所有函數 advices: ["before:printStart", "after"] // 過濾將要執行的鉤子 (可細致到函數名) }) static main() {} // 在執行被打點方法前執行的方法 @Before printStart() { console.log("log:start:", new Date()); } // 可以指定多個方法 @Before printStart2() { console.log("log:start:", new Date()); } // 在執行被打點方法后執行的方法 @After printEnd() { console.log("log:end:", new Date()); } }
必須在切面類的靜態方法上注冊切點Advices(可理解為生命周期,下文用生命周期代指advice)列表
Rockerjs Core 提供了Before, After,After_Throwing, After_Returning,Around等生命周期
Before:在被打點函數執行前執行
After:在被打點函數執行后執行
After_Throwing:在被打點函數拋出異常時執行
After_Returning:在被打點函數返回結果后執行
Around:在被打點函數執行前后執行,類似于 koa 中間件
@After_Returning在 after 后執行
如果原生函數沒有 return 任何東西則不執行
可以修改返回結果
@After_Returning printReturn(ctx, result) { // ctx 為函數執行上下文 // result 為函數執行的結果 result += 666 return result }@After_Throwing
@After_Throwing printthrow(ctx, ex) { // ctx 為函數執行上下文 // ex 為錯誤信息 console.log("Loggy catch: "+ ex.message); console.log(ctx) }@Around
@Around currentTime2(ctx, next) { // ctx 為函數執行上下文 // next 為匹配到的函數 console.log("before",Date.now()); let ret = next(); console.log("after",Date.now(),ret); return ret; }切面組合
我們可以為某個類同時注冊多個切面類,再通過 composeAspects 方法將它們組合起來,默認按照聲明的順序來包裹被打點的函數,最后聲明的類會包裹在最外面一層
@Aspect() class Logger { // ... } @Aspect() class Logger2 { @Pointcut({ clazz: Test, advices: ["before", "after", "around", "after_returning"] }) static main() {} @Before printStart() { console.log("2:start"); } @After printafter() { console.log("2:after"); } @After_Returning printReturn(ctx, result) { console.log("2:after_returning", result) return result + 2 } @Around printAround2(ctx, next) { console.log("2:around:before"); let ret = next(); console.log("2:around:after", ret); return ret; } } @Aspect() class Logger3 { // ... } composeAspects({ clazz: Test, // rules: ".*foo.*", aspects: [Logger, Logger2, Logger3] });
執行結果如下:
3:start 2:start 1:start 3:around:before 2:around:before 1:around:before foo 1:around:after bar 2:around:after bar 3:around:after bar 1:after 2:after 3:after 1:after_returning bar 2:after_returning bar 3:after_returning bar
如果想自定義切面之間執行的順序,可以在切面注解上傳入切面的次序(數值小的在洋蔥模型的外層):
@Aspect({ order: 2 }) class Logger { } @Aspect({ order: 1 }) class Logger2 { } @Aspect({ order: 3 }) class Logger3 { } composeAspects({ clazz: Test, aspects: [Logger, Logger2, Logger3] });
執行順序如下:
2:start 1:start 3:start 2:around:before 1:around:before 3:around:before foo 3:around:after bar 1:around:after bar 2:around:after bar 3:end 1:end 2:end四、異常處理 Exception
除了通過 Rockerjs Core AOP 中的 @After_Throwing 注解來實現錯誤捕獲,我們還提供了更簡便的實現錯誤捕獲的方法,如下例,我們先聲明一個錯誤捕獲夾,然后在被包裹的函數上使用這個錯誤捕獲夾,當函數執行過程中有異常發生時,我們能在捕獲夾的 catch 方法中拿到錯誤信息以及函數執行的上下文。
import { Container, Inject, Catch, Clamp, ExceptionClamp } from "@rockerjs/core"; // 1. 聲明一個捕獲器,實現 catch 方法 @Clamp class Clamper extends ExceptionClamp { catch(ex, ctx) { console.log("hahaha: ****", ex, ctx); } } @Inject class Test { // 2. 使用捕獲器 @Catch("Clamper") test() { throw new Error("12322"); } } Container.getObject("test").test();
與 @After_Throwing 同時使用時,@Catch 會先捕獲到錯誤,再次將錯誤拋出, @After_Throwing 才捕獲到錯誤
@Clamp class Clamper extends ExceptionClamp { catch(ex, ctx) { console.log("hahaha: ****", ex, ctx); throw ex // 將錯誤二次拋出 } } @Inject class Test { @Catch("Clamper") test() { throw new Error("12322"); } } @Aspect class ExceptionClamp2 { @Pointcut({ clazz: Test, advices: ["after_throwing"] }) static main() {} @After_Throwing printThrow(ctx, ex) { console.log("Loggy catch: "+ ex.message); console.log(ctx) } } Container.getObjectContribute("test").test();
請參考 Contribute Guide 后提交 Pull Request。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73829.html
摘要:項目地址項目主頁基于和注解的輕量級容器,提供了依賴注入面向切面編程及異常處理等功能??稍谌我夤こ讨幸?,是一個框架無關的容器。模塊不依賴于任何框架,并與現有框架庫類等保持兼容。 Rockerjs Core 項目地址 項目主頁 基于 TypeScript 和注解的輕量級IoC容器,提供了依賴注入、面向切面編程及異常處理等功能。Rockerjs Core可在任意工程中引入,是一個框架無...
摘要:是一個輕量級的控制反轉和面向切面的容器框架。依賴注入是其一種實現方式目的創建對象并且組裝對象之間的關系擴展理解房屋中介對比自從有了之后,不必自己創建對象了,機制提供了。 使用了Spring,程序員的春天就來了^_^ Spring概念 Spring是什么? struts 是web框架(jsp/action/actionform)hibemate 是 orm 框架,處于持久層Spring ...
摘要:入門篇學習總結時間年月日星期三說明本文部分內容均來自慕課網。主要的功能是日志記錄,性能統計,安全控制,事務處理,異常處理等等。 《Spring入門篇》學習總結 時間:2017年1月18日星期三說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com教學示例源碼:https://github.com/zccodere/s...個人學習源碼:https://git...
摘要:對象之間耦合度過高的系統,必然會出現牽一發而動全身的情形。控制被反轉之后,獲得依賴對象的過程由自身管理變為了由容器主動注入。于是,他給控制反轉取了一個更合適的名字叫做依賴注入。 Spring還可以這么學--IoC(控制反轉) / DI(依賴注入)理解 聲明:文章的前三部分參考博文:https://www.cnblogs.com/Nouno...這篇文章首發是在我的個人微信訂閱號每天學編...
摘要:簡單來說,是一個輕量級的控制反轉和面向切面的容器框架。的基本框架主要包含六大模塊。可以與框架整合。在中是容器的實際代表者。依賴注入在容器創建對象后,處理對象的依賴關系。 目錄 1. Spring開源框架的簡介 2. Spring下IOC容器和DI(依賴注入Dependency injection) 一、Spring開源框架的簡介 ??Spring是一個開源框架,也是于2003 年興...
閱讀 1158·2023-04-26 01:35
閱讀 2513·2021-11-02 14:44
閱讀 7642·2021-09-22 15:38
閱讀 2205·2021-09-06 15:11
閱讀 3720·2019-08-30 15:53
閱讀 795·2019-08-29 16:54
閱讀 631·2019-08-26 13:48
閱讀 1763·2019-08-26 13:47