NestJS 源码解析:装饰器原理
深入 TypeScript 装饰器和 reflect-metadata,揭秘 NestJS 的元数据驱动设计。
装饰器基础
TypeScript 装饰器是一种特殊的声明,可以附加到类、方法、属性或参数上。
类装饰器
function Controller(prefix: string): ClassDecorator {
return (target: Function) => {
Reflect.defineMetadata('path', prefix, target);
};
}
@Controller('cats')
class CatsController {}
方法装饰器
function Get(path: string): MethodDecorator {
return (target, key, descriptor) => {
Reflect.defineMetadata('path', path, descriptor.value);
Reflect.defineMetadata('method', 'GET', descriptor.value);
};
}
class CatsController {
@Get('all')
findAll() {}
}
参数装饰器
function Body(): ParameterDecorator {
return (target, key, index) => {
const existingParams = Reflect.getMetadata('params', target, key) || [];
existingParams.push({ index, type: 'body' });
Reflect.defineMetadata('params', existingParams, target, key);
};
}
class CatsController {
create(@Body() dto: CreateCatDto) {}
}
NestJS 核心装饰器
@Injectable
// packages/common/decorators/core/injectable.decorator.ts
export function Injectable(options?: InjectableOptions): ClassDecorator {
return (target: object) => {
// 标记为可注入
Reflect.defineMetadata(INJECTABLE_WATERMARK, true, target);
// 存储作用域选项
Reflect.defineMetadata(SCOPE_OPTIONS_METADATA, options, target);
};
}
@Controller
// packages/common/decorators/core/controller.decorator.ts
export function Controller(prefixOrOptions?: string | ControllerOptions): ClassDecorator {
const defaultPath = '/';
const [path, host, scopeOptions, versionOptions] = isUndefined(prefixOrOptions)
? [defaultPath, undefined, undefined, undefined]
: isString(prefixOrOptions)
? [prefixOrOptions, undefined, undefined, undefined]
: [
prefixOrOptions.path || defaultPath,
prefixOrOptions.host,
{ scope: prefixOrOptions.scope, durable: prefixOrOptions.durable },
{ version: prefixOrOptions.version },
];
return (target: object) => {
// 标记为控制器
Reflect.defineMetadata(CONTROLLER_WATERMARK, true, target);
// 存储路径
Reflect.defineMetadata(PATH_METADATA, path, target);
// 存储主机
Reflect.defineMetadata(HOST_METADATA, host, target);
// 存储作用域
Reflect.defineMetadata(SCOPE_OPTIONS_METADATA, scopeOptions, target);
// 存储版本
Reflect.defineMetadata(VERSION_METADATA, versionOptions?.version, target);
};
}
@Get/@Post 等
// packages/common/decorators/http/request-mapping.decorator.ts
const createMappingDecorator = (method: RequestMethod) => (path?: string): MethodDecorator => {
return (target, key, descriptor) => {
Reflect.defineMetadata(PATH_METADATA, path || '/', descriptor.value);
Reflect.defineMetadata(METHOD_METADATA, method, descriptor.value);
return descriptor;
};
};
export const Get = createMappingDecorator(RequestMethod.GET);
export const Post = createMappingDecorator(RequestMethod.POST);
export const Put = createMappingDecorator(RequestMethod.PUT);
export const Delete = createMappingDecorator(RequestMethod.DELETE);
export const Patch = createMappingDecorator(RequestMethod.PATCH);
@Inject
// packages/common/decorators/core/inject.decorator.ts
export function Inject<T = any>(token?: T): PropertyDecorator & ParameterDecorator {
return (target: object, key: string | symbol | undefined, index?: number) => {
const type = token || Reflect.getMetadata('design:type', target, key!);
if (!isUndefined(index)) {
// 参数注入
let dependencies = Reflect.getMetadata(SELF_DECLARED_DEPS_METADATA, target) || [];
dependencies = [...dependencies, { index, param: type }];
Reflect.defineMetadata(SELF_DECLARED_DEPS_METADATA, dependencies, target);
} else {
// 属性注入
let properties = Reflect.getMetadata(PROPERTY_DEPS_METADATA, target.constructor) || [];
properties = [...properties, { key, type }];
Reflect.defineMetadata(PROPERTY_DEPS_METADATA, properties, target.constructor);
}
};
}
参数装饰器
@Body/@Query/@Param
// packages/common/decorators/http/route-params.decorator.ts
function createRouteParamDecorator(paramtype: RouteParamtypes) {
return (data?: any): ParameterDecorator =>
(target, key, index) => {
const args = Reflect.getMetadata(ROUTE_ARGS_METADATA, target.constructor, key) || {};
Reflect.defineMetadata(
ROUTE_ARGS_METADATA,
{
...args,
[`${paramtype}:${index}`]: {
index,
data,
pipes: [],
},
},
target.constructor,
key,
);
};
}
export const Body = createRouteParamDecorator(RouteParamtypes.BODY);
export const Query = createRouteParamDecorator(RouteParamtypes.QUERY);
export const Param = createRouteParamDecorator(RouteParamtypes.PARAM);
export const Headers = createRouteParamDecorator(RouteParamtypes.HEADERS);
export const Req = createRouteParamDecorator(RouteParamtypes.REQUEST);
export const Res = createRouteParamDecorator(RouteParamtypes.RESPONSE);
增强器装饰器
@UseGuards
// packages/common/decorators/core/use-guards.decorator.ts
export function UseGuards(...guards: (CanActivate | Function)[]): MethodDecorator & ClassDecorator {
return (
target: any,
key?: string | symbol,
descriptor?: TypedPropertyDescriptor<any>,
) => {
const isMethodDecorator = !!descriptor;
if (isMethodDecorator) {
// 方法级别
const existingGuards = Reflect.getMetadata(GUARDS_METADATA, descriptor.value) || [];
Reflect.defineMetadata(GUARDS_METADATA, [...guards, ...existingGuards], descriptor.value);
} else {
// 类级别
const existingGuards = Reflect.getMetadata(GUARDS_METADATA, target) || [];
Reflect.defineMetadata(GUARDS_METADATA, [...guards, ...existingGuards], target);
}
return descriptor;
};
}
@UsePipes/@UseInterceptors/@UseFilters
实现类似,都是将增强器存储到元数据中。
元数据读取
MetadataScanner
扫描类的所有方法:
// packages/core/metadata-scanner.ts
export class MetadataScanner {
public scanFromPrototype<T, R = any>(
instance: T,
prototype: object,
callback: (name: string) => R,
): R[] {
const methodNames = this.getAllMethodNames(prototype);
return methodNames.map(callback);
}
public getAllMethodNames(prototype: object): string[] {
const isMethod = (prop: string) => {
const descriptor = Object.getOwnPropertyDescriptor(prototype, prop);
return !descriptor?.get && !descriptor?.set && isFunction(prototype[prop]);
};
return Object.getOwnPropertyNames(prototype).filter(
prop => prop !== 'constructor' && isMethod(prop),
);
}
}
PathsExplorer
探索控制器的路由:
// packages/core/router/paths-explorer.ts
export class PathsExplorer {
public scanForPaths(instance: Controller): RouteDefinition[] {
const prototype = Object.getPrototypeOf(instance);
return this.metadataScanner.scanFromPrototype(instance, prototype, method => {
const path = Reflect.getMetadata(PATH_METADATA, prototype[method]);
const requestMethod = Reflect.getMetadata(METHOD_METADATA, prototype[method]);
return {
path: this.validatePath(path),
requestMethod,
targetCallback: prototype[method],
methodName: method,
};
});
}
}
自定义装饰器
组合装饰器
import { applyDecorators, SetMetadata, UseGuards } from '@nestjs/common';
export function Auth(...roles: string[]) {
return applyDecorators(
SetMetadata('roles', roles),
UseGuards(AuthGuard, RolesGuard),
);
}
// 使用
@Auth('admin')
@Get()
findAll() {}
applyDecorators 实现
// packages/common/decorators/core/apply-decorators.ts
export function applyDecorators(
...decorators: Array<ClassDecorator | MethodDecorator | PropertyDecorator>
) {
return <TFunction extends Function, Y>(
target: TFunction | object,
propertyKey?: string | symbol,
descriptor?: TypedPropertyDescriptor<Y>,
) => {
for (const decorator of decorators) {
if (target instanceof Function && !descriptor) {
(decorator as ClassDecorator)(target);
continue;
}
(decorator as MethodDecorator | PropertyDecorator)(
target,
propertyKey!,
descriptor!,
);
}
};
}
自定义参数装饰器
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const User = createParamDecorator(
(data: string, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
return data ? user?.[data] : user;
},
);
// 使用
@Get()
findOne(@User('id') userId: string) {}
createParamDecorator 实现
// packages/common/decorators/http/create-route-param-metadata.decorator.ts
export function createParamDecorator<T = any, I = any, O = any>(
factory: CustomParamFactory<T, I, O>,
): (...dataOrPipes: (Type<PipeTransform> | PipeTransform | T)[]) => ParameterDecorator {
return (...dataOrPipes: (Type<PipeTransform> | PipeTransform | T)[]): ParameterDecorator => {
const [data, ...pipes] = dataOrPipes;
return (target, key, index) => {
const args = Reflect.getMetadata(ROUTE_ARGS_METADATA, target.constructor, key) || {};
const paramtype = uid(21);
Reflect.defineMetadata(
ROUTE_ARGS_METADATA,
{
...args,
[`${paramtype}:${index}`]: {
index,
factory,
data,
pipes,
},
},
target.constructor,
key,
);
};
};
}
元数据常量
// packages/common/constants.ts
export const MODULE_METADATA = {
IMPORTS: 'imports',
PROVIDERS: 'providers',
CONTROLLERS: 'controllers',
EXPORTS: 'exports',
};
export const PATH_METADATA = 'path';
export const METHOD_METADATA = 'method';
export const ROUTE_ARGS_METADATA = '__routeArguments__';
export const GUARDS_METADATA = '__guards__';
export const INTERCEPTORS_METADATA = '__interceptors__';
export const PIPES_METADATA = '__pipes__';
export const EXCEPTION_FILTERS_METADATA = '__exceptionFilters__';
export const INJECTABLE_WATERMARK = '__injectable__';
export const CONTROLLER_WATERMARK = '__controller__';
总结
NestJS 装饰器系统的核心:
- reflect-metadata:存储和读取元数据
- 类装饰器:标记类的角色(Controller、Injectable)
- 方法装饰器:定义路由和增强器
- 参数装饰器:定义参数来源
- 组合装饰器:applyDecorators 组合多个装饰器
- 自定义装饰器:createParamDecorator 创建参数装饰器
下一篇我们将分析路由系统的实现。
📦 源码位置:
packages/common/decorators/下一篇:NestJS 路由系统