开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情
设计
Aop,既是面向切面编程。目前整个AOP的设计如下:
1.分别实现前置处理(@before)和后置处理(@after)
2.匹配规则为方法名称匹配,待完善
3.可实现多个Aop处理,添加index排序参数,如若不传index参数,则使用默认参数进行排序
4.仅对扫描到的component的方法,进行aop匹配
实现
首先,定义类装饰器EnableAspect,标识项目开启Aop功能
@EnableAspect
class App(){
}
而此装饰器的作用,是在application上下文中,将是否启用aop切面参数标记为true
export const EnableAspect = (): ClassDecorator => (targetClass: any) => {
console.log('========================= Enable Aspect============================')
application.isEnableAspect = true
}
以便在后续的初始化工作中,加载代理aop方法到目标函数中 其次,是根据@before 和 @after 装饰器收集各个切面方法
export const Aspect = (): ClassDecorator => {
return (TargetClass: any) => {
application.aspectManager.aspectClassMap.set(TargetClass.name, proxify(new TargetClass()))
}
}
export const AspectMethodKey = 'AspectMethod'
function createAspect(type: string) {
/**
* @param exp:切点表达式<pr>
* @param index:顺序
*/
return (param: { exp: string, index?: number }): MethodDecorator => {
// 所属类,被注解的方法,方法描述符
return (target: any, methodName: string, methodDecorator: PropertyDescriptor) => {
let fn = target[methodName]
const className = target.constructor.name
const aspectInfo: AspectInfo = {
aspectExp: param.exp,
aspectFn: fn,
type,
className,
target,
index: param.index || 100
}
let olds = Reflect.getMetadata(AspectMethodKey, methodDecorator.value) || []
// console.log(olds)
let news = [...olds, aspectInfo]
// console.log(news)
// 在此方法上添加aop信息
Reflect.defineMetadata(AspectMethodKey, news, methodDecorator.value);
}
}
}
export const Before = createAspect('before')
export const After = createAspect('after')
然后,是创建一个AspectManager类,在该类中,定义实现切面应做的各个工作,如注册切面以及,实现代理切面方法等
export interface AspectInfo {
aspectExp: string
aspectFn: any
type: string
className: string
target: any,
index: number
}
export interface MethodAspectsInfo {
before?: AspectInfo[]
after?: AspectInfo[]
}
export class AspectManager {
aspectClassMap: Map<string, any> = new Map()
aspectMethodMap: Map<string, AspectInfo[]> = new Map()
public registerAspect() {
let _this = this
this.aspectClassMap.forEach((instance: any, key: string, map: Map<string, any>) => {
// 实例属性
const proto = Object.getPrototypeOf(instance);
// 方法数组
const functionNameArr = Object.getOwnPropertyNames(proto).filter(
n => n !== 'constructor' && typeof proto[n] === 'function',
);
functionNameArr.forEach(functionName => {
let aspectInfos: AspectInfo[] = Reflect.getMetadata(AspectMethodKey, proto[functionName]);
if (!aspectInfos) return;
aspectInfos.forEach(aspectInfo => {
let as = _this.aspectMethodMap.get(aspectInfo.aspectExp)
if (!as) {
as = []
}
as.push(aspectInfo)
_this.aspectMethodMap.set(aspectInfo.aspectExp, as)
})
})
})
}
public invoke(instance: any, methodName: string): (...args: any[]) => Promise<any> {
// 1.根据方法名获取切面执行数组 befores afters
let isAsyncFunction = util.types.isAsyncFunction(instance[methodName])
let original = instance[methodName]
let as = this.getMethodAspectsInfo(instance, methodName)
if (as.before.length <= 0 && as.after.length <= 0) {
return null
}
let newFn = async function (...args: any[]) {
// console.log('arguments:' + arguments)
// before
for (let b in as.before) {
await as.before[b].aspectFn(...args)
}
// 原方法
let result = await original.apply(this, arguments);
// after
for (let b in as.after) {
await as.after[b].aspectFn({ originalArgs: args, result })
}
return result
}
return newFn;
}
public getMethodAspectsInfo(instance: any, methodName: string) {
let as: MethodAspectsInfo = {
before: new Array(),
after: new Array()
}
application.aspectManager.aspectMethodMap.forEach((value, key, map) => {
// todo:方法匹配优化
//console.log(`=========================methodName:${methodName} --- key:${key} =========================`)
if (methodName == key) {
// console.log('========================= 符合条件 =========================')
for (let i in value) {
let ai = value[i]
if (ai.type == 'before') {
as.before.push(ai)
}
if (ai.type == 'after') {
as.after.push(ai)
}
}
}
})
// 根据index排序
// 从小到大的排序
as.after.sort((Acomponent, Bcomponent) => {
return Acomponent.index - Bcomponent.index;
});
as.before.sort((Acomponent, Bcomponent) => {
return Acomponent.index - Bcomponent.index;
});
return as;
}
}
在application对象实例化时,创建AspectManager实例,并添加至application对象实例上。在组件扫描完成后,便调用AspectManager的registerAspect()方法