Bean是什么
咱们晓得Bean
是Spring
最根底的外围构件,大多数逻辑代码都通过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.atom
、ctx.bean.user
、ctx.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 |
bean | bean组件 |
global | 是否是全局组件 |
应用Bean
1. beanFullName
每一个注册的Bean组件都被调配了全称,具体规定如下
注册名称 | 场景 | 所属模块 | global | beanFullName |
---|---|---|---|---|
test.app | test | test-party | false | test-party.test.app |
testctx | test-party | true | testctx |
全局Bean(global:true
): 当一个Bean组件能够作为一个外围的根底组件的时候,能够设置为全局Bean,不便其余模块的调用,比方:atom
、user
、role
、flow
、flowTask
,等等本地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