当前位置: 首页 > 后端技术 > Node.js

基于原生JS实现的Bean容器和AOP编程

时间:2023-04-03 17:30:13 Node.js

Bean容器和基于原生JS的AOP编程什么是Bean?我们知道Bean是Spring最基础的核心组件,大部分逻辑代码都是通过Bean来管理的。NestJS也基于TypeScript和依赖注入实现了类似于SpringBean的机制:服务提供者(Provider)CabloyJS在原生JS(VanillaJS)上实现了更轻量级和更灵活的bean容器概念CabloyJS设计了??Bean容器机制遵循以下三种概念:1.几乎一切都是Bean。我们大部分的逻辑代码都是通过Bean组件来管理的,比如:Controller、Service、Model、Middleware、Event、Queue、Broadcast、Schedule、Startup、Flow、FlowTask等。CabloyJS4.0实现了Bean容器之后,基本都是核心组件基于Bean进行重构。比如基于EggJS的Controller、Service、Middleware也实现了Bean的组件化。2.Bean支持AOP。所有的Bean组件都可以通过AOP组件进行逻辑扩展。3、AOP也是Bean的一种。组件逻辑扩展的递归设计,为系统的可定制性和可扩展性提供了强大的想象空间。BeanCabloyJS约定了两种定义bean的模式:app和ctx。由于Bean是由容器管理的,所以可以方便的跨模块调用。因此,为了明确Bean的应用场景,一般的约定是:如果Bean只被本模块内部调用,则使用app模式;如果Bean大概率会被其他模块调用,那么就使用ctx模式1.app模式例如:Controller和Service都采用app模式src/module/test-party/backend/src/bean/test.app.jsmodule.exports=app=>{classappBeanextendsapp.meta.BeanBase{actionSync({a,b}){returna+b;}asyncactionAsync({a,b}){returnPromise.resolve(a+b);}}返回appBean;};2。ctx模式如:ctx.bean.atom、ctx.bean。user和ctx.bean.role都采用ctx方式||ctx.module.info.relativeName;}getname(){返回this._name;}设置名称(值){this._name=值;}actionSync({a,b}){返回a+b;}asyncactionAsync({a,b}){returnPromise.resolve(a+b);}}returnctxBean;};ctx.module.info.relativeName:由于ctx模式下的Bean经常被其他模块调用,可以通过该属性获取调用模块的名称来为大部分组件注册bean,EggJS采用约定优先的策略,会在指定位置搜索资源并自动加载并且CabloyJS采用显式注册,让Webpack收集所有后端源码,实现模块编译的特性src/module/test-party/backend/src/beans.jsconsttestApp=require('./bean/test.app.js');consttestCtx=require('./bean/test.ctx.js');module.exports=app=>{constbeans={//测试'test.app':{mode:'app',bean:testApp,},testctx:{模式:'ctx',bean:testCtx,global:true,},};返回豆子;};名称描述modemode:app/ctxbeanbean组件global是使用Bean1的全局组件。beanFullName为每个注册的Bean组件分配一个全名。具体规则如下。注册名场景的模块属于globalbeanFullNametest.apptesttest-partyfalsetest-party.test.apptestctxtest-partytruetestctx全局Bean(global:true):当一个Bean组件可以作为核心基础组件时,可以设置为全局bean,方便其他模块调用,如:atom、user、role、flow、flowTask等。LocalBean(global:false):当一个Bean组件一般只在本模块使用时,可以设置为本地bean,避免命名冲突的场景:对于本地bean,我们一般会分配一个场景名作为前缀,一方面方便bean分类管理,另一方面也便于识别bean的用途另一方面2.基本调用可以直接通过this.ctx.bean获取Bean容器,然后通过beanFullNamesrc/module/test-party/backend/src/controller/test/feat/bean.js获取Bean实例//全局:falsethis.ctx.bean['test-party.test.app'].actionSync({a,b});awaitthis.ctx.bean['test-party.test.app'].actionAsync({a,b});//全局:truethis.ctx.bean.testctx.actionSync({a,b});awaitthis.ctx.bean.testctx.actionAsync({a,b});3.通过this.ctx创建一个新的Bean实例.bean获取Bean实例,那么这个实例就是当前ctx的单例如果需要创建一个新的Bean实例,可以这样进行:ctx.bean._newBean(beanFullName,...args)比如我们要创建一个新的Flow实例:src/module-system/a-flow/backend/src/bean/bean.flow.js_createFlowInstance({flowDef}){constflowInstance=ctx.bean._newBean(`${moduleInfo.relativeName}.local.flow.flow`,{flowDef,});返回流实例;}4。跨模块调用LocalBean本地Bean也可以被跨模块调用跨模块调用的本质:新建一个ctx上下文环境,这个ctx的模块信息和本地Bean保持一致,然后调用本地Bean通过新容器ctx.beanawaitctx.executeBean({locale,subdomain,beanModule,beanFullName,context,fn,transaction})nameoptionaldescriptionlocaleoptionaldefaultequaltoctx.localessubdomainoptionaldefaultequaltoctx.subdomainbeanModulerequiredlocalbeanmodulenamebeanFullNamerequired本地bean全名context可选calllocalbean当时传入的参数fn必须调用本地Bean的方法名。交易是可选的。是否启用数据库事务。比如我们要调用模块本地Bean的a-file:service.file,直接上传用户头像,返回downloadUrlsrc/module-system/a-base-sync/backend/src/bean/bean.user.js//上传constres2=awaitctx.executeBean({beanModule:'a-file',beanFullName:'a-file.service.file',context:{fileContent:res.data,meta,user:null},fn:'_upload',});//保存profile._avatar=res2.downloadUrl;5、app.beanctx.bean为每个请求初始化一个容器,app.bean可以实现整个应用使用一个容器,从而实现Bean组件src/module/test-party/的应用级单例模式后端/src/controller/test/feat/bean.jsapp.bean['test-party.test.app'].actionSync({a,b});awaitapp.bean['test-party.test.app'].actionAsync({a,b});AOP编程篇幅有限,关于AOP编程请参考:cabloy-aop相关链接官网:https://cabloy.com/GitHub:https://github.com/zhennann/cabloy

最新推荐
猜你喜欢