RockerjsCore项目地址项目主页是一个基于TypeScript和注解的轻量级IoC容器,提供依赖注入、面向切面编程、异常处理等功能处理。RockerjsCore可以在任何项目中引入,是一个独立于框架的IoC容器。@rockerjs/core模块不依赖于任何框架,保持与现有框架、库、类等的兼容。通过DI(DependencyInjection)实现代码解耦和依赖解耦,保证构建复杂应用时的可扩展性和灵活性;同时提供二维编程的能力,基于注解,可以在每个连接点进行非核心业务(Advice)操作,减少代码冗余;最后,它提供了一种简单的基于注解配置的异常处理机制——Clamp机制,通过匹配特定规则的异常处理器来实现处理。1.快速开始安装npminstall@rockerjs/core@rockerjs/core最佳实践需要结合TypeScript装饰器使用(或者接口也可以),所以需要在项目中添加一个tsconfig.json文件根目录和配置编译选项“experimentalDecorators”和“emitDecoratorMetadata”为真示例1import{Container,Inject}from'@rockerjs/core';classUser{id:string="testId";name:string="testName";}classUserService{getUser(_id:string):User{returnnewUser();}}@InjectclassControlDefault{@InjectuserService:UserService;test(){让用户:User=this.userService.getUser("test");console.log(user);}}@Inject('controller-with-args',newDate())classControlDefaultWithArgs{name:string;time:Date;constructor(name:string,time:Date){这个.name=name;this.time=time;}@InjectuserService:UserService;test(){让用户:User=this.userService.getUser("test");console.log(user,this.name,this.时间);}}@Inject('controller1','util',newDate())classControl{name:string;时间:日期;构造函数(n名称:字符串,时间:日期){this.name=name;this.time=时间;}@Inject用户服务:用户服务;test(){让用户:User=this.userService.getUser("test");安慰。日志(用户,this.name,this.time);}}//通过getObject接口从容器中获取实例,参数为“单例名称”(默认名称为类名首字母小写)Container.getObject('controlDefault').test();//通过getObject接口从容器中获取实例,本例不提供实例名Container.getObject('controlDefaultWithArgs').test();//通过getObjectinterface从容器中获取一个实例。在此示例中,提供了3个参数。@rockerjs/core认为第一个参数是实例名,其余参数用于实例化Container.getObject('controllor1').test();例2:RPCimport{Container,Inject}from'@rockerjs/core';//PRCDemo实现letRPC={config:function(cfg:{serviceUrl:string,interfaces:Function[]}){if(cfg.interfaces){cfg.interfaces.forEach((type:FunctionConstructor)=>{if(type.prototype){letnewObj={},proto=type.prototype;letnms=Object.getOwnPropertyNames(原型);if(nms){nms.forEach((nm)=>{if(nm!='constructor'&&typeof(proto[nm])==='function'){newObj[nm]=function(){//{nm:方法名,arguments:参数表},而是调用远程请求过程returnarguments[0];//测试返回}}})}Container.provides([type,()=>{returnnewObj;}])}})}}}//--DEMO--------------------------------------------------------//1。接口声明(注意,这里只能使用Concrete类)classProduct{getById(id:string):string{returnnull;}}//2。ApplyRPCFrameworkRPC.config({serviceUrl:null,interfaces:[Product]//在RPC中提供接口描述和buildfactory})//3。服务类@InjectclassService{@Injectproduct:Product;test(){让id:string='tid';让rst=this.product.getById(id);控制台日志(第一个);}}//4。测试Container.getObject('service').test();2.依赖注入和容器依赖注入@Inject提供注解@Inject来实现依赖注入,当我们有如下GetDubboData类时classGetDubboData{p0:number;构造函数(p0:数字,p1:字符串){this.p0=p0;我们可以通过如下方式实例化这个类,同时传入指定的参数,直接传入构造函数类SomeControl的参数{@Inject(1,'aaa')privatedubbo:GetDubboData}工厂函数类SomeControl{@Inject(function(){return[1,'aaa']})privatedubbo:GetDubboData}None构造函数或参数为空classSomeControl{@Injectprivatedubbo:GetDubboData}操作类实例化的默认实例化方法容器可以满足开发者的大部分需求。RockerjsCore提供了自定义实例化工厂的方法,也提供了获取类映射的方法和类实例化函数注册和修改类的实例化方法直接传递给类或工厂函数//形式一:likeContainer.provides(classextendsUserService{})Container.provides(classextendsUserService{getUser(id:string):User{console.log(1);returnnewUser();}});传入类和类的工厂函数//形式2:likeContainer.provides([UserService,FactoryFunction])Container.provides([UserService,()=>{returnnewclassextendsUserService{getUser(id:string):User{console.log(2);returnnewUser();}}();}]);getinstantiationmethodregistrygetGeneralHashmap()返回一张构造函数-工厂方法映射表,结构如下constglobalGeneralProviders:Map=newMap();手动实例化方法Container.injectClazzManually方法提供了注册表中类的直接实例化函数,参数是构造函数和你要在classSomeControl中传递的参数{transGet:GetTransData=Container.injectClazzManually(GetTransData,1,2);asyncgetProduct(_productId?:number){让json:any=awaitthis.transGet。getDetail(_productId);控制台日志(json);}}完整的例子假设我们有一个getasync数据抽象类abstractclassGetTransData{p0:numberconstructor(p0:number,p1:string){console.log(p0+p1)this.p0=p0}abstractasyncgetDetail(_proId:number):Promise;}通过容器Container.provides([GetTransData,(_p0,_p1)=>{returnnewclassextendsGetTransData{constructor(p0:number,p1:string){super(p0,p1);}asyncgetDetail(_id:number):Promise{await((ms)=>newPromise(res=>setTimeout(res,ms)))(100)返回`你好${this.p0}`}}(_p0,_p1);}]);最后在测试类中通过@Inject方法实例化对象注入@InjectclassSomeControl{@Inject(666,2)transGet:GetTransData;asyncgetProduct(_productId?:number){让json:any=awaitthis.transGet.getDetail(_productId);控制台日志(json);}}Container.getObject('someControl').getProduct();得到输出结果668Hello666ProgrammingAOP面向切面编程(AOP就是AspectOrientedProgram)的首字母缩写,是指在运行时将代码动态切割成类指定方法和位置的编程思想RockerjsCore提供了AOP编程能力的简单示例。如果我们想在下面的foo方法执行前后运行classTest{foo(){console.log('foo')}}newTest().foo(),我们可以声明一个日志类声明它通过@Aspect注解作为切面类,通过@Pointcut注解配置要执行的类、方法、hook,最后通过@Before和@After注解在对应的生命周期中识别修改的方法Import{Aspect,Pointcut,Before,After}from"@rockerjs/core";@AspectclassLogger{//切入点必须注册到静态方法上@Pointcut({clazz:Test,//定位修改类规则:".*foo.*",//通过正则化匹配对应的方法,不填则匹配所有函数。advices:["before:printStart","after"]//过滤要执行的钩子(可以详细到函数名)})staticmain(){}//在执行标记方法之前执行的方法@BeforeprintStart(){console.log("log:start:",newDate());}//可以指定多个方法@BeforeprintStart2(){console.log("log:start:",newDate());}//执行点分方法后执行的方法@AfterprintEnd(){console.log("log:end:",newDate());}}切入点Advices必须注册在切面类的静态方法上(可以理解为一个生命周期,生命周期指的是下面的advice)。RockerjsCore提供了Before、After、After_Throwing、After_Returning、Around等生命周期Before:在点函数执行之前执行After:在点函数执行完之后执行After_Throwing:在点函数抛出异常时执行After_Returning:在dotted函数返回结果后执行Around:在dotted函数执行前后执行,类似koa中间件@After_Returningexecutesafterafter如果native函数没有返回任何东西,则不会执行你可以修改returnresult@After_ReturningprintReturn(ctx,result){//ctx是函数执行上下文//result是函数执行的结果result+=666returnresult}@After_Throwing@After_Throwingprintthrow(ctx,ex){//ctx是函数执行context//ex是错误消息控制台。log('Loggycatch:'+ex.message);console.log(ctx)}@Around@AroundcurrentTime2(ctx,next){//ctx是函数执行上下文//next是匹配的函数console.log('before',Date.now());让ret=next();console.log('之后',Date.now(),ret);returnret;}切面组合我们可以同时为一个类注册多个Section类,然后通过composeAspects方法组合起来。默认情况下,虚线函数按照声明的顺序进行包裹,最后声明的类会包裹在最外层@Aspect()classLogger{//...}@Aspect()classLogger2{@Pointcut({clazz:测试,建议:["before","after","around",'after_returning']})staticmain(){}@BeforeprintStart(){console.log("2:start");}@Afterprintafter(){console.log("2:after");}@After_ReturningprintReturn(ctx,result){console.log('2:after_returning',result)返回结果+2}@AroundprintAround2(ctx,next){console.log("2:around:before");让ret=next();console.log("2:around:after",ret);返还;}}@Aspect()classLogger3{//...}composeAspects({clazz:Test,//rules:'.*foo.*',aspects:[Logger,Logger2,Logger3]});执行结果如下:3:start2:start1:start3:around:before2:around:before1:around:beforefoo1:around:afterbar2:around:afterbar3:around:afterbar1:after2:after3:after1:after_returningbar2:after_returningbar3:after_returningbar如果要自定义切片的执行顺序,可以在切片注解中传入切片顺序(值小的在洋葱模型外层):@Aspect({order:2})classLogger{}@Aspect({order:1})classLogger2{}@Aspect({order:3})classLogger3{}composeAspects({clazz:Test,aspects:[Logger,Logger2,记录器3]});执行顺序如下:2:start1:start3:start2:around:before1:around:before3:around:beforefoo3:around:afterbar1:around:afterbar2:around:afterbar3:end1:end2:end4.异常处理Exception除了在RockerjsCoreAOP中通过@After_Throwing注解实现错误捕获外,我们还提供了更简单的方法来实现错误捕获。在下面的例子中,我们首先声明了一个错误捕获文件夹,然后将其包装在函数的函数上使用这个错误捕获文件夹。当函数执行过程中发生异常时,我们可以在capture文件夹的catch方法中获取错误信息和函数执行的上下文。import{Container,Inject,Catch,Clamp,ExceptionClamp}from"@rockerjs/core";//1.声明一个捕手并实现catch方法@ClampclassClamperextendsExceptionClamp{catch(ex,ctx){console.log("哈哈哈:****",ex,ctx);}}@InjectclassTest{//2.使用捕手@Catch("Clamper")test(){thrownewError("12322");}}容器。getObject<测试>('测试').测试();与@After_Throwing同时使用时,@Catch会先捕获错误,再次抛出错误,然后@After_Throwing再捕获错误@ClampclassClamperextendsExceptionClamp{catch(ex,ctx){console.log("哈哈哈:****",例如,ctx);throwex//两次抛出错误}}@InjectclassTest{@Catch("Clamper")test(){thrownewError("12322");}}@AspectclassExceptionClamp2{@Pointcut({clazz:Test,advices:['after_throwing']})staticmain(){}@After_ThrowingprintThrow(ctx,ex){console.log('Loggycatch:'+ex.信息);console.log(ctx)}}Container.getObject('test').test();贡献请在参考贡献指南后提交合并请求。