From 0340d937c0fae5e1f0dabf429a624a81b1ab4e95 Mon Sep 17 00:00:00 2001 From: kiwi-jang Date: Mon, 6 Nov 2023 17:48:25 +0900 Subject: [PATCH] refactor: Improve modularity by splitting AutoAspectExecutor --- src/auto-aspect-executor.ts | 123 ++++++++++++++++++++---------------- src/core/types.ts | 6 ++ src/create-decorator.ts | 6 +- 3 files changed, 77 insertions(+), 58 deletions(-) diff --git a/src/auto-aspect-executor.ts b/src/auto-aspect-executor.ts index 0d934ea..ddf6fbe 100644 --- a/src/auto-aspect-executor.ts +++ b/src/auto-aspect-executor.ts @@ -2,7 +2,7 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { DiscoveryService, MetadataScanner, Reflector } from '@nestjs/core'; import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper'; import { ASPECT } from './aspect'; -import { AnyFunction } from './core/types'; +import { AopMetadata } from './core/types'; import { LazyDecorator } from './lazy-decorator'; /** @@ -18,6 +18,10 @@ export class AutoAspectExecutor implements OnModuleInit { ) {} onModuleInit() { + this.bootstrapLazyDecorators(); + } + + private bootstrapLazyDecorators() { const controllers = this.discoveryService.getControllers(); const providers = this.discoveryService.getProviders(); @@ -30,66 +34,77 @@ export class AutoAspectExecutor implements OnModuleInit { .concat(controllers) .filter(({ instance }) => instance && Object.getPrototypeOf(instance)); - for (const wrapper of instanceWrappers) { - const target = wrapper.isDependencyTreeStatic() - ? wrapper.instance - : wrapper.metatype.prototype; + for (const lazyDecorator of lazyDecorators) { + for (const wrapper of instanceWrappers) { + this.applyLazyDecorator(lazyDecorator, wrapper); + } + } + } - // Use scanFromPrototype for support nestjs 8 - const methodNames = this.metadataScanner.scanFromPrototype( - target, - wrapper.isDependencyTreeStatic() ? Object.getPrototypeOf(target) : target, - (name) => name, + private applyLazyDecorator(lazyDecorator: LazyDecorator, instanceWrapper: InstanceWrapper) { + const target = instanceWrapper.isDependencyTreeStatic() + ? instanceWrapper.instance + : instanceWrapper.metatype.prototype; + + // Use scanFromPrototype for support nestjs 8 + const methodNames = this.metadataScanner.scanFromPrototype( + target, + instanceWrapper.isDependencyTreeStatic() ? Object.getPrototypeOf(target) : target, + (name) => name, + ); + + const metadataKey = this.reflector.get(ASPECT, lazyDecorator.constructor); + // instance에 method names 를 순회하면서 lazyDecorator.wrap을 적용함 + for (const methodName of methodNames) { + const metadataList: AopMetadata[] = this.reflector.get( + metadataKey, + target[methodName], ); + if (!metadataList) { + return; + } - for (const methodName of methodNames) { - lazyDecorators.forEach((lazyDecorator) => { - const metadataKey = this.reflector.get(ASPECT, lazyDecorator.constructor); - - const metadataList: { - originalFn: AnyFunction; - metadata?: unknown; - aopSymbol: symbol; - }[] = this.reflector.get< - { - originalFn: AnyFunction; - metadata?: unknown; - aopSymbol: symbol; - }[] - >(metadataKey, target[methodName]); - if (!metadataList) { - return; - } - - for (const item of metadataList) { - const { originalFn, metadata, aopSymbol } = item; - - // eslint-disable-next-line @typescript-eslint/no-this-alias - const self = this; - function wrap(this: object, ...args: unknown[]) { - const cached = self.wrappedMethodCache.get(item); - if (cached) { - return cached.apply(this, args); - } - - const wrappedMethod = lazyDecorator.wrap({ - instance: this, - methodName, - method: originalFn.bind(this), - metadata, - }); - self.wrappedMethodCache.set(this, wrappedMethod); - return wrappedMethod.apply(this, args); - } - - target[aopSymbol] ??= {}; - target[aopSymbol][methodName] = wrap; - } - }); + for (const aopMetadata of metadataList) { + this.wrapMethod({ lazyDecorator, aopMetadata, methodName, target }); } } } + private wrapMethod({ + lazyDecorator, + aopMetadata, + methodName, + target, + }: { + lazyDecorator: LazyDecorator; + aopMetadata: AopMetadata; + methodName: string; + target: any; + }) { + const { originalFn, metadata, aopSymbol } = aopMetadata; + + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + function wrappedFunction(this: object, ...args: unknown[]) { + const cached = self.wrappedMethodCache.get(aopMetadata); + if (cached) { + return cached.apply(this, args); + } + + const wrappedMethod = lazyDecorator.wrap({ + instance: this, + methodName, + method: originalFn.bind(this), + metadata, + }); + self.wrappedMethodCache.set(this, wrappedMethod); + return wrappedMethod.apply(this, args); + } + + target[aopSymbol] ??= {}; + target[aopSymbol][methodName] = wrappedFunction; + } + private lookupLazyDecorators(providers: InstanceWrapper[]): LazyDecorator[] { const { reflector } = this; diff --git a/src/core/types.ts b/src/core/types.ts index 284bdd8..b88de51 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -1 +1,7 @@ export type AnyFunction = (...args: unknown[]) => unknown; + +export type AopMetadata = { + originalFn: AnyFunction; + metadata?: unknown; + aopSymbol: symbol; +}; diff --git a/src/create-decorator.ts b/src/create-decorator.ts index 77c30cf..0330000 100644 --- a/src/create-decorator.ts +++ b/src/create-decorator.ts @@ -1,4 +1,5 @@ import { applyDecorators } from '@nestjs/common'; +import { AopMetadata } from './core/types'; import { AddMetadata } from './utils'; /** @@ -13,10 +14,7 @@ export const createDecorator = ( return applyDecorators( // 1. Add metadata to the method (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => { - return AddMetadata< - symbol | string, - { metadata?: unknown; aopSymbol: symbol; originalFn: unknown } - >(metadataKey, { + return AddMetadata(metadataKey, { originalFn: descriptor.value, metadata, aopSymbol,