当前位置: 首页 > news >正文

【NestJS】Reflect Metadata 全解

🧩 一、Reflect Metadata 是什么?

元数据(metadata)就是 ——

“附加在类、方法、参数、属性等上的隐藏信息”。

这些信息不会改变代码逻辑
但可以被框架(比如 Nest)在后期拿出来“做事”。

举个简单例子:

class UserController {@Get('users')getUsers() {}
}

这里的 @Get('users') 本质上不是注册接口,
只是调用了一个函数,在这个方法上打了标记

Reflect.defineMetadata('path', 'users', target.getUsers);
Reflect.defineMetadata('method', 'GET', target.getUsers);

然后 Nest 启动时再通过:

Reflect.getMetadata('path', target.getUsers);
Reflect.getMetadata('method', target.getUsers);

去拿出这些元数据,再真正注册到路由系统里。


⚙️ 二、Reflect.defineMetadata()Reflect.getMetadata() 用法

✅ 定义元数据

Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey?);
  • metadataKey:字符串或 Symbol,用来标识数据。
  • metadataValue:要保存的任意值(可以是对象、数组、字符串等)。
  • target:要绑定的目标(类、方法、属性)。
  • propertyKey(可选):要绑定到哪个属性或方法上。

✅ 读取元数据

Reflect.getMetadata(metadataKey, target, propertyKey?);
  • 参数同上,读取之前写入的元数据。

🧠 三、最小示例理解

import 'reflect-metadata'; // 必须引入一次class MyClass {}// 定义元数据
Reflect.defineMetadata('role', 'admin', MyClass);// 读取元数据
console.log(Reflect.getMetadata('role', MyClass)); // 输出 'admin'

这说明你可以把一些“额外信息”藏到类或函数上。


🔍 四、在方法或属性上使用

class UserService {@Reflect.metadata('cache', true)getUsers() {}
}// 等价于:
Reflect.defineMetadata('cache', true, UserService.prototype, 'getUsers');// 获取时:
const value = Reflect.getMetadata('cache', UserService.prototype, 'getUsers');
console.log(value); // true

🧩 五、嵌套用法(例如在装饰器中)

通常装饰器都是利用这两函数在背后干活:

function MyDecorator(path: string) {return (target: any, key: string, descriptor: PropertyDescriptor) => {Reflect.defineMetadata('path', path, descriptor.value);};
}class MyController {@MyDecorator('/users')getUsers() {}
}console.log(Reflect.getMetadata('path', MyController.prototype.getUsers)); // '/users'

这就是 Nest @Get() 的简化版。


🧩 六、组合使用(Controller + Method)

这也是 NestJS 路由系统的经典套路 👇

// Controller 装饰器
function Controller(prefix: string) {return (target: any) => {Reflect.defineMetadata('prefix', prefix, target);};
}// Method 装饰器
function Get(path: string) {return (target: any, key: string, descriptor: PropertyDescriptor) => {Reflect.defineMetadata('path', path, descriptor.value);Reflect.defineMetadata('method', 'GET', descriptor.value);};
}@Controller('users')
class UserController {@Get(':id')getUser() {}
}// Nest 启动阶段读取:
const prefix = Reflect.getMetadata('prefix', UserController);
const path = Reflect.getMetadata('path', UserController.prototype.getUser);
const method = Reflect.getMetadata('method', UserController.prototype.getUser);console.log(`${method} /${prefix}/${path}`); // GET /users/:id

🧩 七、Reflect.getOwnMetadata() vs getMetadata()

方法是否会查原型链典型用途
getMetadata()✅ 会沿原型链向上查找常用于装饰器系统(如继承时)
getOwnMetadata()❌ 只查当前对象常用于判断当前类是否定义过

🧩 八、Reflect.hasMetadata()Reflect.deleteMetadata()

方法作用
Reflect.hasMetadata(key, target, propertyKey?)判断是否存在该元数据(包含原型链)
Reflect.hasOwnMetadata(key, target, propertyKey?)只查自己
Reflect.deleteMetadata(key, target, propertyKey?)删除定义的元数据

示例:

Reflect.hasMetadata('path', MyController.prototype.getUser); // true
Reflect.deleteMetadata('path', MyController.prototype.getUser);
Reflect.hasMetadata('path', MyController.prototype.getUser); // false

🧩 九、真实 Nest 内部例子

Nest 的 @Controller()@Get() 等装饰器底层其实就是这样写的:

export const PATH_METADATA = 'path';
export const METHOD_METADATA = 'method';export function Controller(path: string): ClassDecorator {return (target) => {Reflect.defineMetadata(PATH_METADATA, path, target);};
}export function Get(path: string): MethodDecorator {return (target, key, descriptor) => {Reflect.defineMetadata(PATH_METADATA, path, descriptor.value);Reflect.defineMetadata(METHOD_METADATA, 'GET', descriptor.value);};
}

Nest 在启动时读取:

const controllerPath = Reflect.getMetadata(PATH_METADATA, ControllerClass);
const methodPath = Reflect.getMetadata(PATH_METADATA, methodHandler);
const httpMethod = Reflect.getMetadata(METHOD_METADATA, methodHandler);

然后注册成路由。


🧩 十、你可以自己实现一套小型 Nest 路由系统

import 'reflect-metadata';const PATH = 'path';
const METHOD = 'method';function Controller(prefix: string) {return (target: any) => Reflect.defineMetadata(PATH, prefix, target);
}function Route(method: string, path: string) {return (target: any, key: string, descriptor: PropertyDescriptor) => {Reflect.defineMetadata(PATH, path, descriptor.value);Reflect.defineMetadata(METHOD, method, descriptor.value);};
}const Get = (path: string) => Route('GET', path);
const Post = (path: string) => Route('POST', path);@Controller('users')
class UserController {@Get('/')getAll() {}@Post('/')create() {}
}// 启动阶段自动打印
for (const key of Object.getOwnPropertyNames(UserController.prototype)) {const handler = UserController.prototype[key];const method = Reflect.getMetadata(METHOD, handler);const path = Reflect.getMetadata(PATH, handler);if (method && path) {const prefix = Reflect.getMetadata(PATH, UserController);console.log(`[${method}] /${prefix}${path}`);}
}

输出:

[GET] /users/
[POST] /users/

这几乎就是 Nest 的控制器注册原理!


✅ 总结表

函数作用是否查原型链
defineMetadata(key, value, target, prop?)定义元数据
getMetadata(key, target, prop?)获取元数据✅ 是
getOwnMetadata(key, target, prop?)获取自身定义的元数据❌ 否
hasMetadata(key, target, prop?)是否存在(包含继承)✅ 是
hasOwnMetadata(key, target, prop?)是否存在(不含继承)❌ 否
deleteMetadata(key, target, prop?)删除元数据

http://www.dtcms.com/a/525991.html

相关文章:

  • 如何做搜索引擎网站淄博 做网站
  • 网站开发怎么开发公司简介宣传文案
  • 在REBa2Cu3O7−δ块状超导磁体优异性能的可靠外延生长中,缓冲层辅助生长架构的进展 项目文献
  • 小米商城的网站建站淄博外贸网站哪家好
  • Jetson orin agx配置ultralytics 使用docker或conda
  • 营子区住房和城乡建设局网站做东南亚跨境电商平台有哪些
  • lesson76:Vue.js 核心特性详解:事件处理、计算属性与侦听器
  • 不申请域名可以 做网站吗短期职业技能培训班
  • 北京专业网站开发公司wordpress+没有+sql
  • 如何做关于旅游的网站页面怎么查询个人名下营业执照
  • 兴国做网站招聘网站建设推广
  • 下载男女做爰免费网站租一个服务器要多少钱
  • 又在今年1024时
  • wordpress 最简单模板下载wordpress seo怎么写
  • wdcp 快速迁移网站佛山电商网站制作团队
  • CI/CD(二)—— Git 基础操作全攻略:从入门到实战
  • 建设一个网站需要那些技术做网站的职位
  • OK432(AbMole,M9414)
  • 三种 Badcase 精度验证方案详解与 hbm_infer 部署实录
  • 全网门户网站制做互联网行业现状分析
  • 网站安全和信息化建设衣服网站模板
  • 古腾堡布局的网站平面设计做画册用网站
  • 仿魔客吧网站模板cms客户管理系统
  • SpringAI + DeepSeek本地大模型应用开发-智能会话
  • 可视化响应式网站建设下载别人网站的asp
  • ps4gta5网站建设中网站建设硬件计划
  • Windows安装Git教程
  • 付网站建设服务费什么科目专门培训seo的网站
  • 专门做app的网站厦门网站制作哪里好薇
  • WebPages Razor:现代Web开发的新利器