EdwardZZZ / articles

工作点滴记录
2 stars 0 forks source link

AOP 完整实现 #57

Open EdwardZZZ opened 5 years ago

EdwardZZZ commented 5 years ago
type JoinPoint = {
    args: any[],
    proceed: Function,
};

export class Aspect {
    readonly target: any;

    constructor(target: any) {
        this.target = target;
    }
}

export default function AOP(aspect: any): Function {
    if (!aspect.prototype || !(aspect.prototype instanceof Aspect)) {
        throw new Error('AOP arguments must be extends Aspect.');
    }

    return function aopDecorator(target: Function, methodName: string, desc: PropertyDescriptor) {
        if (!methodName) {
            Reflect.ownKeys(target.prototype).forEach((method: string) => {
                if (method === 'constructor') return;
                const aopMethod = aopDecorator(target, method, Reflect.getOwnPropertyDescriptor(target.prototype, method));
                Reflect.defineProperty(target.prototype, method, aopMethod);
            });

            return;
        }

        const { value: method, configurable, enumerable } = desc;
        return {
            configurable,
            enumerable,
            writable: true,
            value: async function aop(...args: any[]) {
                const aopInstance = Reflect.construct(aspect, [this]);
                const { Before, After, AfterReturning, AfterThrowing, Around } = aopInstance;
                let methodResult: any;

                const proceed = async () => {
                    if (Before) {
                        const beforeResult = await Promise.resolve(Reflect.apply(Before, aopInstance, []));
                        if (beforeResult === false) return;
                    }

                    if (AfterThrowing) {
                        try {
                            methodResult = await Promise.resolve(Reflect.apply(method, this, args));
                        } catch (err) {
                            AfterThrowing(err);
                        }
                    } else {
                        methodResult = await Promise.resolve(Reflect.apply(method, this, args));
                    }

                    return methodResult;
                };

                if (Around) {
                    const point: JoinPoint = {
                        args,
                        proceed,
                    };

                    await Around(point);
                } else {
                    await proceed();
                }

                if (After) {
                    await Promise.resolve(Reflect.apply(After, aopInstance, []));
                }

                if (AfterReturning) {
                    await Promise.resolve(Reflect.apply(AfterReturning, aopInstance, [methodResult]));
                }
            },
        };
    };
}
EdwardZZZ commented 5 years ago
class TestAspect extends Aspect {
    Before() {
        console.log('aspect before');
    }
    After() {
        console.log('aspect after');
    }
    AfterReturning(result: any) {
        console.log('aspect AfterReturning', result);
    }
    AfterThrowing(err: Error) {
        console.log(err);
    }
    async Around({ args, proceed }: any) {
        console.log('>>around args', ...args);
        const result = await proceed(...args);
        console.log('》》', result);
        console.log('>>around after');
    }
}

class Test {
    @AOP(TestAspect)
    test(n: number) {
        console.log('--method test.--');
        // throw new Error('======');
        return n;
    }
}

const test = new Test();

test.test(111);