Introduction
VonaJS AOP programming includes three capabilities:
Controller Aspect: Applies logic to controller methodsInternal Aspect: Applies logic to any method of any class within a classExternal Aspect: Applies logic to any method of any class from the outside without changing the class source code
Controller Aspect
Controller Aspect List
- Middleware
- Guard
- Interceptor
- Pipe
- Filter
Execution Sequence Diagram
The execution sequence diagram for controller aspects is as follows:

Onion Model:MiddlewareandInterceptorsupport theOnion Model, allowing aspect logic to be executed before and afterController ActionMiddleware: Three types of Middleware are provided for different execution sequence stages:Middleware System,Middleware Global, andMiddleware Local, allowing for more refined aspect logicRoute Match: Only theMiddleware Systemis executed before route matching; the rest are executed afterFilter: If an exception is thrown at any stage,Filterwill be executed to customize the processing logic oferror informationanderror log
Internal Aspect
Internal aspects provide two mechanisms: AOP Method and Magic Method
1. AOP Method
Implement aspect logic directly on Class Method through decorator
Example: Database Transaction
diff
class ServiceStudent {
+ @Core.transaction()
async update(id: TableIdentity, student: DtoStudentUpdate) {
return await this.scope.model.student.updateById(id, student);
}
}@Core.transaction: A decorator implemented through theAOP Methodmechanism that can directly provide database transaction capabilities
Example: Logging
diff
class ServiceStudent {
+ @Core.log()
async update(id: TableIdentity, student: DtoStudentUpdate) {
return await this.scope.model.student.updateById(id, student);
}
}@Core.log: A decorator implemented using theAOP Methodmechanism directly provides logging capabilities
2. Magic Method
Dynamic properties or methods can be implemented through __get__ and __set__ within the Class
Example: Obtaining a model instance
diff
class ServiceStudent {
async update(id: TableIdentity, student: DtoStudentUpdate) {
+ return await this.scope.model.student.updateById(id, student);
}
}this.scope.model.xxx: Instead of usingdependency injection,dependency lookupis used to obtain the model instance directly from the scope object, simplifying the code writing style
Implementation Idea
The system provides a ServiceModelResolver class for dynamic model instance resolution. The code is as follows:
typescript
class ServiceModelResolver {
protected __get__(prop: string) {
const beanFullName = `${this[SymbolModuleScope]}.model.${prop}`;
return this.bean._getBean(beanFullName as any);
}
}- When
this.scope.model.studentis called, the__get__method is automatically executed, passing theprop: 'student'parameter - The
propparameter is combined with the current module name to formbeanFullName - The model instance is retrieved from the global container using
beanFullNameand returned to the caller
External Aspect
Using the update method of the ServiceStudent class as an example, we implement logging capabilities through the external aspect:
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: This decorator is used to implement theexternal aspectmatch: This option is used to associate theAopLogclass with theServiceStudentclass, which beanFullName isdemo-student.service.studentupdate: Provides theupdatemethod with the same name to implement custom logic