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,等等

CabloyJS 4.0在实现了Bean容器之后,基本上所有外围组件都以Bean为根底进行了重构。比方基于EggJS的Controller、Service、Middleware,也实现了Bean组件化

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;};
ctx.module.info.relativeName: 因为ctx模式的Bean常常被其余模块调用,那么能够通过此属性获得调用方模块的名称

注册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/ctx
beanbean组件
global是否是全局组件

应用Bean

1. beanFullName

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

注册名称场景所属模块globalbeanFullName
test.apptesttest-partyfalsetest-party.test.app
testctxtest-partytruetestctx
全局Bean(global:true): 当一个Bean组件能够作为一个外围的根底组件的时候,能够设置为全局Bean,不便其余模块的调用,比方: atomuserroleflowflowTask,等等

本地Bean(global:false): 当一个Bean组件个别只用于本模块时,能够设置为本地Bean,从而防止命名抵触

场景:对于本地Bean,咱们个别为其调配一个场景名称作为前缀,一方面便于Bean的分类管理,另一方面也便于辨识Bean的用处

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也能够被跨模块调用

跨模块调用的实质:新建一个ctx上下文环境,该ctx的module信息与本地Bean统一,而后通过新容器ctx.bean来调用本地Bean
await ctx.executeBean({ locale, subdomain, beanModule, beanFullName, context, fn, transaction })
名称可选阐明
locale可选默认等于ctx.locale
subdomain可选默认等于ctx.subdomain
beanModule必须本地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

相干链接

  • 官网: https://cabloy.com/
  • GitHub: https://github.com/zhennann/cabloy