最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 基于原生JS实现的Bean容器和AOP编程

    正文概述 掘金(zhennann)   2021-01-06   497

    Bean是什么

    我们知道BeanSpring最基础的核心构件,大多数逻辑代码都通过Bean进行管理。NestJS基于TypeScript依赖注入也实现了类似于Spring Bean的机制:服务提供者(Provider)

    CabloyJS则是在原生JS(Vanilla JS)上实现了更轻量、更灵活的Bean容器

    理念

    CabloyJS在设计Bean容器机制时,遵循了以下3个理念:

    1. 几乎所有事物都是Bean

    我们绝大多数逻辑代码都通过Bean组件进行管理,比如:Controller、Service、Model、Middleware、Event、Queue、Broadcast、Schedule、Startup、Flow、Flow Task,等等

    2. Bean支持AOP

    所有Bean组件都可以通过AOP组件进行逻辑扩展

    3. AOP也是一种Bean

    AOP组件既然也是Bean,那么也可以通过其他AOP组件进行逻辑扩展

    定义Bean

    CabloyJS约定了两种定义Bean的模式:app和ctx。由于Bean被容器托管,可以很方便的跨模块调用。因此,为了清晰的辨识Bean被应用的场景,一般约定:如果Bean只被本模块内部调用,那么就使用app模式;如果大概率会被其他模块调用,那么就使用ctx模式

    1. app模式

    比如:Controller、Service都采用app模式

    src/module/test-party/backend/src/bean/test.app.js

    module.exports = app => {
    
      class appBean extends app.meta.BeanBase {
    
        actionSync({ a, b }) {
          return a + b;
        }
    
        async actionAsync({ a, b }) {
          return Promise.resolve(a + b);
        }
    
      }
    
      return appBean;
    };
    

    2. ctx模式

    比如:ctx.bean.atomctx.bean.userctx.bean.role都采用ctx模式

    src/module/test-party/backend/src/bean/test.ctx.js

    module.exports = ctx => {
      class ctxBean {
    
        constructor(moduleName) {
          this._name = moduleName || ctx.module.info.relativeName;
        }
    
        get name() {
          return this._name;
        }
    
        set name(value) {
          this._name = value;
        }
    
        actionSync({ a, b }) {
          return a + b;
        }
    
        async actionAsync({ a, b }) {
          return Promise.resolve(a + b);
        }
    
      }
    
      return ctxBean;
    };
    
    

    注册Bean

    对于大多数组件,EggJS采用约定优先的策略,会在指定的位置查找资源,并自动加载。而CabloyJS采用显式注册,从而Webpack可以收集所有后端源码,实现模块编译的特性

    src/module/test-party/backend/src/beans.js

    const testApp = require('./bean/test.app.js');
    const testCtx = require('./bean/test.ctx.js');
    
    module.exports = app => {
      const beans = {
        // test
        'test.app': {
          mode: 'app',
          bean: testApp,
        },
        testctx: {
          mode: 'ctx',
          bean: testCtx,
          global: true,
        },
      };
      return beans;
    };
    
    名称说明
    mode模式:app/ctxbeanbean组件global是否是全局组件

    使用Bean

    1. beanFullName

    每一个注册的Bean组件都被分配了全称,具体规则如下

    注册名称场景所属模块globalbeanFullName
    test.apptesttest-partyfalsetest-party.test.apptestctxtest-partytruetestctx

    2. 基本调用

    可以直接通过this.ctx.bean取得Bean容器,然后通过beanFullName获取Bean实例

    src/module/test-party/backend/src/controller/test/feat/bean.js

    
      // global: false
      this.ctx.bean['test-party.test.app'].actionSync({ a, b }); 
      await this.ctx.bean['test-party.test.app'].actionAsync({ a, b });
    
      // global: true
      this.ctx.bean.testctx.actionSync({ a, b });
      await this.ctx.bean.testctx.actionAsync({ a, b });
    

    3. 新建Bean实例

    通过this.ctx.bean获取Bean实例,那么这个实例对当前ctx而言是单例的。如果需要新建Bean实例,可以按如下方式进行:

    ctx.bean._newBean(beanFullName, ...args)
    

    比如我们要新建一个Flow实例:

    src/module-system/a-flow/backend/src/bean/bean.flow.js

        _createFlowInstance({ flowDef }) {
          const flowInstance = ctx.bean._newBean(`${moduleInfo.relativeName}.local.flow.flow`, {
            flowDef,
          });
          return flowInstance;
        }
    

    4. 跨模块调用本地Bean

    本地Bean也可以被跨模块调用

    await ctx.executeBean({ locale, subdomain, beanModule, beanFullName, context, fn, transaction })
    
    名称可选说明
    locale可选默认等于ctx.localesubdomain可选默认等于ctx.subdomainbeanModule必需本地Bean所属模块名称beanFullName必需本地Bean的全称context可选调用本地Bean时传入的参数fn必需调用本地Bean的方法名transaction可选是否要启用数据库事务

    比如我们要调用模块a-file的本地Bean: service.file,直接上传用户的avatar,并返回downloadUrl

    src/module-system/a-base-sync/backend/src/bean/bean.user.js

          // upload
          const res2 = await ctx.executeBean({
            beanModule: 'a-file',
            beanFullName: 'a-file.service.file',
            context: { fileContent: res.data, meta, user: null },
            fn: '_upload',
          });
          // hold
          profile._avatar = res2.downloadUrl;
    

    5. app.bean

    ctx.bean是每个请求初始化一个容器,而app.bean则可以实现整个应用使用一个容器,从而实现Bean组件的应用级别的单例模式

    src/module/test-party/backend/src/controller/test/feat/bean.js

      app.bean['test-party.test.app'].actionSync({ a, b }); 
      await app.bean['test-party.test.app'].actionAsync({ a, b });
    

    AOP编程

    限于篇幅,关于AOP编程请参见:cabloy-aop

    相关链接

    • [官网: cabloy.com/](https://c…
    • [GitHub: github.com/zhennann/ca…

    起源地下载网 » 基于原生JS实现的Bean容器和AOP编程

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元