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
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,方便其他模块的调用,比如: 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

相关链接

基于原生JS实现的Bean容器和AOP编程的更多相关文章

  1. 用jQuery基于原生js封装的轮播

    我发现轮播在很多网站里面都用到过,一个绚丽的轮播可以为网页增色不少,最近闲来无事,也用原生js封装了一个轮播,可能不像网上的插件那么炫,但是也有用心去做.主要用了闭包的思想.需要传递的参数有:图片地址 ...

  2. 基于原生js的图片延迟加载

    当页面图片比较多的时候,我们通常会做一个延迟加载,避免页面打开时一下子的请求数太多,加载过慢影响用户体验. 如果项目用了jquery框架,则可以直接用 jquery.lazyload.可在jquery ...

  3. 基于原生js的返回顶部组件,兼容主流浏览器

    基于原生js的返回顶部插件,兼容IE8及以上.FF.chrome等主流浏览器. js文件中封装了getScrollTop()和changeScrollTop()函数分别用于获取滚动条滚动的高度和修改滚 ...

  4. 基于原生JS的jsonp方法的实现

    基于原生JS的jsonp方法的实现 jsonp,相信大家并不陌生,是在js异步请求中解决跨域的方法之一,原理很简单,有不清楚的同学可以google下,这里就补详细解释了.在Jquery库中,jQuer ...

  5. 基于原生JS封装数组原型上的sort方法

    基于原生JS封装数组原型上的sort方法 最近学习了数组的原型上内置方法的封装,加强了用原生JS封装方法的能力,也进一步理解数组方法封装的过程,实现的功能.虽然没有深入底层,了解源码.以下解法都是基于 ...

  6. 基于three.js实现特定Div容器的粒子特效封装

    本文基于three.js实现特定容器的粒子特效效果,支持用户传入特定的dom对象以及粒子颜色. 效果图 移入库 <script src="jquery-1.11.3.min.js&qu ...

  7. 前后端数据交互处理基于原生JS模板引擎开发

    json数据错误处理,把json文件数据复制到----> https://www.bejson.com/ 在线解析json 这样能直观的了解到是否是json数据写错,在控制台打断点,那里错误打那 ...

  8. 转载 -- 基于原生JS与OC方法互相调用并传值(附HTML代码)

    最近项目里面有有个商品活动界面,要与web端传值,将用户在网页点击的商品id 传给客户端,也就是js交互,其实再说明白一点就是方法的互相调用而已. 本文叙述下如何进行原生的JavaScript交互 本 ...

  9. js:基于原生js的上啦下啦刷新功能

    链接:https://www.jianshu.com/p/a8392115e6f0演示地址:http://wonghan.cn/iscroll-demo/html:<body> <d ...

随机推荐

  1. [MySQL]IP处理函数inet_aton()和inet_ntoa()

    INET_ATON(expr) 给出一个作为字符串的网络地址的"点地址"(如127.0.0.1)表示,返回一个代表该地址数值的整数.地址可以是4或8比特地址. mysql> ...

  2. 计算机网络 TCP 四次挥手过程和状态变迁

    客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN 报文,之后客户端进入 FIN_WAIT_1 状态. 服务端收到该报文后,就向客户端发送 ACK 应答报 ...

  3. c++对c的拓展_指针的引用

    套用引用公式:Type & ref =val; 假设:type 类型为int * 由公式得 int * & ref = val; // int * *const ref=&va ...

  4. webpack打包学习

    从上图我们可以看出,webpack 可以将多种静态资源 js.css.sass文件等转换成一个静态文件,以此可以减少页面的请求,从而提高浏览器响应速度 1.安装开发依赖包 npm install we ...

  5. SpringBoot-总结

    SpringBoot一站式开发 官网:https://spring.io/projects/spring-boot Spring Boot可以轻松创建独立的.基于Spring的生产级应用程序,它可以让 ...

  6. Java学习day27

    今天跟着做了一个模拟龟兔赛跑的程序 只有一条赛道,乌龟和兔子在同一条赛道上比赛,使用了多线程 为了实现兔子睡觉,在run方法内增加了当前奔跑者是否是兔子的判断且当前奔跑步数是否是10的整数倍的判断,如 ...

  7. 不借助 Javascript,利用 SVG 快速构建马赛克效果

    之前在公众号转发了好友 Vajoy 的一篇文章 -- 巧用 CSS 把图片马赛克风格化. 核心是利用了 CSS 中一个很有意思的属性 -- image-rendering,它可以用于设置图像缩放算法. ...

  8. 机器学习实战:用SVD压缩图像

    前文我们了解了奇异值分解(SVD)的原理,今天就实战一下,用矩阵的奇异值分解对图片进行压缩. Learn by doing 我做了一个在线的图像压缩应用,大家可以感受一下. https://huggi ...

  9. vscode golang 不能自动补全问题

    问题描述: 使用vscode编辑go语言时,有时候会莫名其妙的代码不能自动补全,struct的属性值不能自动提示,这时候如果重新启动vscode也没有效果,就可能是gocode插件出了问题或者有了更新 ...

  10. 攻防世界-MISC:SimpleRAR

    这是攻防世界新手练习区的第十题,题目如下: 点击下载附件1,是一个RAR文件,解压一下,得到flag.txt文件,激动的点进去看一下 好吧,就知道没有这么简单,用010editor打开这个RAR文件, ...