如果用过NestJS框架都知道,在NestJS框架中AOP编程包括以下几个能力:Middleware、Guard、Interceptor、Pipe、Filter。事实上AOP编程的应用场景更广泛,上述所列5个能力仅仅是AOP编程的子集。下面,我们看看在VonaJS框架中,AOP编程是怎样的。
VonaJS AOP编程
VonaJS AOP 编程包括三个方面的能力:
- 控制器切面: 为 Controller 方法切入逻辑
- 内部切面: 在 Class 内部,为任何 Class 的任何方法切入逻辑
- 外部切面: 在不改变 Class 源码的前提下,从外部为任何 Class 的任何方法切入逻辑
控制器切面
控制器切面清单
- Middleware
- Guard
- Intercepter
- Pipe
- Filter
执行时序图
控制器切面的执行时序图如下:
- 洋葱模型: Middleware和Intercepter支持洋葱模型,允许在Controller Action之前和之后执行切面逻辑
- Middleware: 针对不同的执行时序节点,系统提供了三种 Middleware: Middleware System、Middleware Global和Middleware Local,从而可以实现更精细化的切面逻辑
- Route Match: 只有Middleware System在路由匹配之前执行,其余在路由匹配之后执行
- Filter: 任何环节抛出异常,都会执行Filter,从而自定义错误信息和错误日志的处理逻辑
内部切面
内部切面提供两个机制:AOP Method和魔术方法
1. AOP Method
直接在 Class Method 上通过装饰器切入逻辑
举例:数据库事务
- class ServiceStudent {
- + @Database.transaction()
- async update(id: TableIdentity, student: DtoStudentUpdate) {
- return await this.scope.model.student.updateById(id, student);
- }
- }
复制代码
- @Database.transaction:通过AOP Method机制实现的装饰器,可以直接提供数据库事务能力
举例:日志
- class ServiceStudent {
- + @Log()
- async update(id: TableIdentity, student: DtoStudentUpdate) {
- return await this.scope.model.student.updateById(id, student);
- }
- }
复制代码
- @Log:通过AOP Method机制实现的装饰器,可以直接提供日志能力
2. 魔术方法
可以在 Class 内部通过__get__和__set__切入动态属性或方法
举例:获取 model 实例
- class ServiceStudent {
- async update(id: TableIdentity, student: DtoStudentUpdate) {
- + return await this.scope.model.student.updateById(id, student);
- }
- }
复制代码
- this.scope.model.xxx: 没有使用依赖注入,而是使用依赖查找,直接通过 scope 对象获取 model 实例,从而简化代码的书写风格
实现思路
系统提供了一个 Class ServiceModelResolver,用于实现 model 实例的动态解析,代码如下:- class ServiceModelResolver {
- protected __get__(prop: string) {
- const beanFullName = `${this[SymbolModuleScope]}.model.${prop}`;
- return this.bean._getBean(beanFullName as any);
- }
- }
复制代码
- 当调用this.scope.model.student时,会自动执行__get__方法,并且传入参数prop: 'student'
- 将参数prop与当前模块名称合并成beanFullName
- 通过beanFullName从全局容器中获取 model 实例,并返回给调用者
外部切面
仍以 Class ServiceStudent的update方法为例,通过外部切面来实现日志能力:- import { Aop } from 'vona-module-a-aspect';
- @Aop({ match: 'demo-student.service.student' })
- class AopLog {
- async update(_args: Parameters, next: Function, _receiver: any) {
- const timeBegin = Date.now();
- const res = await next();
- const timeEnd = Date.now();
- console.log('time: ', timeEnd - timeBegin);
- return res;
- }
- }
复制代码
- @Aop: 此装饰器用于实现外部切面
- match: 用于将 Class AopLog与 Class ServiceStudent关联,ServiceStudent的 beanFullName 是demo-student.service.student
- update: 在AopLog中提供与ServiceStudent同名的方法update,实现自定义逻辑即可
资源
- Github:https://github.com/vonajs/vona
- 文档:https://vona.js.org/
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |