当前位置: 首页 > news >正文

VonaJS AOP编程大杀器:外部切面

在VonaJS框架中,AOP编程包括三方面:控制器切面、内部切面和外部切面。

控制器切面: 为 Controller 方法切入逻辑,包括:Middleware、Guard、Interceptor、Pipe和Filter

内部切面: 在 Class 内部,为任何 Class 的任何方法切入逻辑,包括:AOP Method和魔术方法

外部切面: 在不改变 Class 源码的前提下,从外部为任何 Class 的任何方法切入逻辑

VonaJS中的外部切面,可以类比于Spring Boot中的AOP切面和AOP织入概念。VonaJS的外部切面不需要什么前置通知、后置通知、异常通知和环绕通知,只需提供一个同名方法就可以了。之所以可以这么简洁,是因为使用了洋葱圈模型。

此外,VonaJS的外部切面支持完整的类型推断与智能代码提示,开发体感比Spring Boot优雅太多。

下面,我们就来考察一下VonaJS的外部切面到底是个什么样?为什么可以成为AOP编程的🚀大杀器🔪

创建目标Class

可以针对任何 Class 实现外部切面。下面,以 Service 为例,在模块 demo-student 中创建一个 Service test,代码如下:

@Service()

export class ServiceTest extends BeanBase {

private _name: string;

protected __init__() {

this._name = '';

}

protected async __dispose__() {

this._name = '';

}

get name() {

return this._name;

}

set name(value) {

this._name = value;

}

actionSync(a: number, b: number) {

return a + b;

}

async actionAsync(a: number, b: number) {

return Promise.resolve(a + b);

}

}

创建外部切面

接下来,创建一个外部切面log,为 Class ServiceTest的属性和方法分别提供扩展逻辑

1. Cli命令

$ vona :create:bean aop log --module=demo-student

2. 菜单命令

右键菜单 - [模块路径]: Vona Aspect/Aop

AOP定义

import { BeanAopBase } from 'vona';

import { Aop } from 'vona-module-a-aspect';

@Aop({ match: 'demo-student.service.test' })

export class AopLog extends BeanAopBase {}

@Aop: 此装饰器用于实现外部切面

match: 用于将 Class AopLog与 Class ServiceTest关联,ServiceTest的 beanFullName 是demo-student.service.test

名称 类型 说明

match string|regexp|(string|regexp)[] 针对哪些 Class 启用

切面:同步方法

为ServiceTest#actionSync输出运行时长

在 VSCode 编辑器中,输入代码片段aopactionsync,自动生成代码骨架:

action: AopAction<ClassSome, 'action'> = (_args, next, _receiver) => {

return next();

};

调整代码,然后添加 log 逻辑

actionSync: AopAction<ServiceTest, 'actionSync'> = (_args, next, _receiver) => {

const timeBegin = Date.now();

const res = next();

const timeEnd = Date.now();

console.log('actionSync: ', timeEnd - timeBegin);

return res;

};

actionSync: 提供与ServiceTest同名的方法actionSync

切面:异步方法

为ServiceTest#actionAsync输出运行时长

在 VSCode 编辑器中,输入代码片段aopaction,自动生成代码骨架:

action: AopAction<ClassSome, 'action'> = async (_args, next, _receiver) => {

return await next();

};

调整代码,然后添加 log 逻辑

actionAsync: AopAction<ServiceTest, 'actionAsync'> = async (_args, next, _receiver) => {

const timeBegin = Date.now();

const res = await next();

const timeEnd = Date.now();

console.log('actionAsync: ', timeEnd - timeBegin);

return res;

};

actionAsync: 提供与ServiceTest同名的方法actionAsync

切面:getter

为ServiceTest#get name输出运行时长

在 VSCode 编辑器中,输入代码片段aopgetter,自动生成代码骨架:

protected __get_xxx__: AopActionGetter<ClassSome, 'xxx'> = function (next, _receiver) {

const value = next();

return value;

};

调整代码,然后添加 log 逻辑

protected __get_name__: AopActionGetter<ServiceTest, 'name'> = function (next, _receiver) {

const timeBegin = Date.now();

const value = next();

const timeEnd = Date.now();

console.log('get name: ', timeEnd - timeBegin);

return value;

};

__get_name__: 对应ServiceTest的 getter 方法get name

切面:setter

为ServiceTest#set name输出运行时长

在 VSCode 编辑器中,输入代码片段aopsetter,自动生成代码骨架:

protected __set_xxx__: AopActionSetter<ClassSome, 'xxx'> = function (value, next, _receiver) {

return next(value);

}

调整代码,然后添加 log 逻辑

protected __set_name__: AopActionSetter<ServiceTest, 'name'> = function (value, next, _receiver) {

const timeBegin = Date.now();

const res = next(value);

const timeEnd = Date.now();

console.log('set name: ', timeEnd - timeBegin);

return res;

};

__set_name__: 对应ServiceTest的 setter 方法set name

切面:__init__

为ServiceTest#__init__输出运行时长

在 VSCode 编辑器中,输入代码片段aopinit,自动生成代码骨架:

protected __init__: AopActionInit<ClassSome> = (_args, next, _receiver) => {

next();

};

调整代码,然后添加 log 逻辑

protected __init__: AopActionInit<ServiceTest> = (_args, next, _receiver) => {

const timeBegin = Date.now();

next();

const timeEnd = Date.now();

console.log('__init__: ', timeEnd - timeBegin);

};

__init__: 提供与ServiceTest同名的方法__init__

切面:__dispose__

为ServiceTest#__dispose__输出运行时长

在 VSCode 编辑器中,输入代码片段aopdispose,自动生成代码骨架:

protected __dispose__: AopActionDispose<ClassSome> = async (_args, next, _receiver) => {

await next();

};

调整代码,然后添加 log 逻辑

protected __dispose__: AopActionDispose<ServiceTest> = async (_args, next, _receiver) => {

const timeBegin = Date.now();

await next();

const timeEnd = Date.now();

console.log('__dispose__: ', timeEnd - timeBegin);

};

__dispose__: 提供与ServiceTest同名的方法__dispose__

切面:__get__

为ServiceTest扩展魔术方法

参见: 魔术方法

在 VSCode 编辑器中,输入代码片段aopget,自动生成代码骨架:

protected __get__: AopActionGet<ClassSome> = (_prop, next, _receiver) => {

const value = next();

return value;

};

调整代码,然后添加自定义字段red

protected __get__: AopActionGet<ServiceTest> = (prop, next, _receiver) => {

if (prop === 'red') return '#FF0000';

const value = next();

return value;

};

__get__: 约定的魔术方法名称

通过接口类型合并的机制为颜色提供类型定义

declare module 'vona-module-demo-student' {

export interface ServiceTest {

red: string;

}

}

切面:__set__

为ServiceTest扩展魔术方法

参见: 魔术方法

在 VSCode 编辑器中,输入代码片段aopset,自动生成代码骨架:

protected __set__: AopActionSet<ClassSome> = (_prop, value, next, _receiver) => {

return next(value);

};

调整代码,为自定义字段red设置值

private _colorRed: string | undefined;

protected __set__: AopActionSet<ServiceTest> = (prop, value, next, _receiver) => {

if (prop === 'red') {

this._colorRed = value;

return true;

}

return next(value);

};

__set__: 约定的魔术方法名称

如果为prop设置了值,返回true,否则调用next方法

然后调整__get__的逻辑:

protected __get__: AopActionGet<ServiceTest> = (prop, next, _receiver) => {

- if (prop === 'red') return '#FF0000';

+ if (prop === 'red') return this._colorRed;

const value = next();

return value;

}

切面:__method__

为ServiceTest的任何方法扩展逻辑

在 VSCode 编辑器中,输入代码片段aopmethod,自动生成代码骨架:

protected __method__: AopActionMethod<ClassSome> = (_method, _args, next, _receiver) => {

return next();

};

调整代码,然后为方法actionSync和actionAsync添加 log 逻辑

protected __method__: AopActionMethod<ServiceTest> = (method, _args, next, _receiver) => {

if (method !== 'actionSync' && method !== 'actionAsync') {

return next();

}

const timeBegin = Date.now();

function done(res) {

const timeEnd = Date.now();

console.log(`method ${method}: `, timeEnd - timeBegin);

return res;

}

const res = next();

if (res?.then) {

return res.then((res: any) => {

return done(res);

});

}

return done(res);

};

__method__: 约定的魔术方法名称

res?.then: 判断返回值是否是 Promise 对象,进行不同处理,从而兼容同步方法和异步方法

AOP顺序

针对同一个目标 Class,可以关联多个 AOP。所以,VonaJS 提供了两个参数,用于控制 AOP 的执行顺序

1. dependencies

比如,还有一个 AOP demo-student:log3,我们希望执行顺序如下:demo-student:log3 > Current

@Aop({

match: 'demo-student.service.test',

+ dependencies: 'demo-student:log3',

})

class AopLog {}

2. dependents

dependents的顺序刚好与dependencies相反,我们希望执行顺序如下:Current > demo-student:log3

@Aop({

match: 'demo-student.service.test',

+ dependents: 'demo-student:log3',

})

class AopLog {}

AOP启用/禁用

可以控制 AOP 的启用/禁用

1. Enable

src/backend/config/config/config.ts

// onions

config.onions = {

aop: {

'demo-student:log': {

+ enable: false,

},

},

};

2. Meta

可以让 AOP 在指定的运行环境生效

名称 类型 说明

flavor string|string[] 参见: 运行环境与Flavor

mode string|string[] 参见: 运行环境与Flavor

举例

@Aop({

+ meta: {

+ flavor: 'normal',

+ mode: 'dev',

+ },

})

class AopLog {}

查看当前生效的AOP清单

可以直接在目标 Class action 中输出当前生效的 AOP 清单

class ServiceTest {

protected async __dispose__() {

+ this.bean.onion.aop.inspect();

this._name = '';

}

this.bean.onion: 取得全局 Service 实例 onion

.aop: 取得与 AOP 相关的 Service 实例

.inspect: 输出当前生效的 AOP 清单

当方法被执行时,会自动在控制台输出当前生效的 AOP 清单,效果如下:

http://www.cnnetsun.cn/news/174957.html

相关文章:

  • 计算机Java毕设实战-基于springboot村委办公管理系统 基于SpringBoot的乡村事务综合服务平台的设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • Java毕设选题推荐:基于springboot的村务管理系统的设计与实现智慧村务管理系统的设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 老派C++程式設計師 vs. 現代C++程式設計師:類型系統觀念的全面戰爭
  • 2025年论文去AI率工具合集:每天5次免费AIGC查重+1500字免费降AI!
  • MySQL 千万级表变更字段,要想不锁表,可以这么做!
  • 【毕业设计】基于springboot的校园零售管理系统的设计与实现(源码+文档+远程调试,全bao定制等)
  • 硬件自查自纠!十年前的电脑可能还可以再战十年
  • 一键配置 Web 前端开发环境(PowerShell 自动化脚本)
  • 程序员必备技能:AI Agent 9种设计模式深度解析,提升大模型应用效能(值得收藏)
  • 【python大数据毕设实战】哮喘患者症状数据可视化分析系统、Hadoop、计算机毕业设计、包括数据爬取、数据分析、数据可视化、机器学习
  • 9 个降AI率工具,MBA 必备避坑指南
  • Windows系统文件inetmib1.dll丢失损坏 下载修复方法
  • Boost电路的右半平面零点
  • 【全球AI伦理治理】
  • 毕业季必看!7款免费AI写论文神器实测,一站式搞定选题、大纲到降重
  • LLMs之Survey之Agent:《Measuring Agents in Production》翻译与解读
  • 零代码上手Google Gemini 3:5种实用方法大揭秘
  • “你用的那个AI,到底把你坑了还是救了?”——解锁宏智树论文的协作新范式
  • 好写作AI:别等学校采购了!你的论文“救命神器”自己就能用上
  • Windows系统文件GdiPlus.dll丢失或损坏 下载修复方法
  • 研究生必备8款AI写论文神器:5分钟生成25000字问卷类论文,自动生成高信度数据
  • 【BuildFlow 筑流】unitrix_macros库 Cargo.toml 配置详解及依赖库用法
  • 《开发者出海必看:如何优雅地搞定海外服务支付?(保姆级干货)》
  • Thinkphp和Laravel企业防爆安全设备信息系统
  • Thinkphp和Laravel全家桶鲜花售卖商城系统vue
  • 记录我适配iOS26遇到的一些问题
  • 通过命令模拟pod创建
  • 同步机无感 STM32 低成本 MD500E 永磁同步控制方案大揭秘
  • 小宝玩具 【通达信、源码 、主图、附图】
  • 使用 Github Pages 和 Hexo