介绍
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 上通过装饰器切入逻辑
举例:数据库事务
diff
class ServiceStudent {
+ @Database.transaction()
async update(id: TableIdentity, student: DtoStudentUpdate) {
return await this.scope.model.student.update({
...student,
id,
});
}
}
@Database.transaction
:通过AOP Method
机制实现的装饰器,可以直接提供数据库事务能力
举例:日志
diff
class ServiceStudent {
+ @Log()
async update(id: TableIdentity, student: DtoStudentUpdate) {
return await this.scope.model.student.update({
...student,
id,
});
}
}
@Log
:通过AOP Method
机制实现的装饰器,可以直接日志能力
2. 魔术方法
可以在 Class 内部通过__get__
和__set__
切入动态属性或方法
举例:获取 model 实例
diff
class ServiceStudent {
async update(id: TableIdentity, student: DtoStudentUpdate) {
+ return await this.scope.model.student.update({
...student,
id,
});
}
}
this.scope.model.xxx
: 没有使用依赖注入
,而是使用依赖查找
,直接通过 scope 对象获取 model 实例,从而简化代码的书写风格
实现思路
系统提供了一个 Class ServiceModelResolver
,用于实现 model 实例的动态解析,代码如下:
typescript
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
方法为例,通过外部切面
来实现日志能力:
typescript
import { Aop } from 'vona-module-a-aspect';
@Aop({ match: 'demo-student.service.student' })
class AopLog {
async update(_args: Parameters<any>, 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
: 用于将 ClassAopLog
与 ClassServiceStudent
关联,ServiceStudent
的 beanFullName 是demo-student.service.student
update
: 在AopLog
中提供与ServiceStudent
同名的方法update
,实现自定义逻辑即可