您好,登錄后才能下訂單哦!
這篇文章主要講解了“reflect metadata Nest實現原理是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“reflect metadata Nest實現原理是什么”吧!
Nest 是 Node.js 的服務端框架,它最出名的就是 IOC(inverse of control) 機制了,也就是不需要手動創建實例,框架會自動掃描需要加載的類,并創建他們的實例放到容器里,實例化時還會根據該類的構造器參數自動注入依賴。
比如入口 Module 里引入某個模塊的 Module:
import { Module } from '@nestjs/common'; import { CatsModule } from './cats/cats.module'; @Module({ imports: [CatsModule], }) export class AppModule {}
然后這個模塊的 Module 里會聲明 Controller 和 Service:
import { Module } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Module({ controllers: [CatsController], providers: [CatsService], }) export class CatsModule {}
Controller 里就是聲明 url 對應的處理邏輯:
import { Body, Controller, Get, Param, Post } from '@nestjs/common'; import { CatsService } from './cats.service'; import { CreateCatDto } from './dto/create-cat.dto'; @Controller('cats') export class CatsController { constructor(private readonly catsService: CatsService) {} @Post() async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); } @Get() async findAll(): Promise<Cat[]> { return this.catsService.findAll(); } }
這個 CatsController 的構造器聲明了對 CatsService 的依賴:
然后 CatsService 里就可以去操作數據庫進行增刪改查了。
這里簡單實現一下:
import { Injectable } from '@nestjs/common'; import { Cat } from './interfaces/cat.interface'; @Injectable() export class CatsService { private readonly cats: Cat[] = []; create(cat: Cat) { this.cats.push(cat); } findAll(): Cat[] { return this.cats; } }
之后在入口處調用 create 把整個 nest 應用跑起來:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); } bootstrap();
然后瀏覽器訪問下我們寫的那個 controller 對應的 url,打個斷點:
你會發現 controller 的實例已經創建了,而且 service 也給注入了。這就是依賴注入的含義。
這種機制就叫做 IOC(控制反轉),也叫依賴注入,好處是顯而易見的,就是只需要聲明依賴關系,不需要自己創建對象,框架會掃描聲明然后自動創建并注入依賴。
Java 里最流行的 Spring 框架就是 IOC 的實現,而 Nest 也是這樣一個實現了 IOC 機制的 Node.js 的后端框架。
不知道大家有沒有感覺很神奇,只是通過裝飾器聲明了一下,然后啟動 Nest 應用,這時候對象就給創建好了,依賴也給注入了。
那它是怎么實現的呢?
大家如果就這樣去思考它的實現原理,還真不一定能想出來,因為缺少了一些前置知識。也就是實現 Nest 最核心的一些 api: Reflect 的 metadata 的 api。
有的同學會說,Reflect 的 api 我很熟呀,就是操作對象的屬性、方法、構造器的一些 api:
比如 Reflect.get 是獲取對象屬性值
Reflect.set 是設置對象屬性值
Reflect.has 是判斷對象屬性是否存在
Reflect.apply 是調用某個方法,傳入對象和參數
Reflect.construct 是用構造器創建對象實例,傳入構造器參數
這些 api 在 MDN 文檔里可以查到,因為它們都已經是 es 標準了,也被很多瀏覽器實現了。
但是實現 Nest 用到的 api 還沒有進入標準,還在草案階段,也就是 metadata 的 api:
它有這些 api:
Reflect.defineMetadata(metadataKey, metadataValue, target); Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey); let result = Reflect.getMetadata(metadataKey, target); let result = Reflect.getMetadata(metadataKey, target, propertyKey);
Reflect.defineMetadata 和 Reflect.getMetadata 分別用于設置和獲取某個類的元數據,如果最后傳入了屬性名,還可以單獨為某個屬性設置元數據。
存在類或者對象上呀,如果給類或者類的靜態屬性添加元數據,那就保存在類上,如果給實例屬性添加元數據,那就保存在對象上,用類似 [[metadata]] 的 key 來存的。
這有啥用呢?
看上面的 api 確實看不出啥來,但它也支持裝飾器的方式使用:
@Reflect.metadata(metadataKey, metadataValue) class C { @Reflect.metadata(metadataKey, metadataValue) method() { } }
Reflect.metadata 裝飾器當然也可以再封裝一層:
function Type(type) { return Reflect.metadata("design:type", type); } function ParamTypes(...types) { return Reflect.metadata("design:paramtypes", types); } function ReturnType(type) { return Reflect.metadata("design:returntype", type); } @ParamTypes(String, Number) class Guang { constructor(text, i) { } @Type(String) get name() { return "text"; } @Type(Function) @ParamTypes(Number, Number) @ReturnType(Number) add(x, y) { return x + y; } }
然后我們就可以通過 Reflect metadata 的 api 或者這個類和對象的元數據了:
let obj = new Guang("a", 1); let paramTypes = Reflect.getMetadata("design:paramtypes", obj, "add"); // [Number, Number]
這里我們用 Reflect.getMetadata 的 api 取出了 add 方法的參數的類型。
上面就是 @Module 裝飾器的實現,里面就調用了 Reflect.defineMetadata 來給這個類添加了一些元數據。
所以我們這樣用的時候:
import { Module } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Module({ controllers: [CatsController], providers: [CatsService], }) export class CatsModule {}
其實就是給 CatsModule 添加了 controllers 的元數據和 providers 的元數據。
后面創建 IOC 容器的時候就會取出這些元數據來處理:
而且 @Controller 和 @Injectable 的裝飾器也是這樣實現的:
看到這里,大家是否想明白 nest 的實現原理了呢?
其實就是通過裝飾器給 class 或者對象添加元數據,然后初始化的時候取出這些元數據,進行依賴的分析,然后創建對應的實例對象就可以了。
所以說,nest 實現的核心就是 Reflect metadata 的 api。
當然,現在 metadata 的 api 還在草案階段,需要使用 reflect-metadata 這個 polyfill 包才行。
其實還有一個疑問,依賴的掃描可以通過 metadata 數據,但是創建的對象需要知道構造器的參數,現在并沒有添加這部分 metadata 數據呀:
比如這個 CatsController 依賴了 CatsService,但是并沒有添加 metadata 呀:
import { Body, Controller, Get, Param, Post } from '@nestjs/common'; import { CatsService } from './cats.service'; import { CreateCatDto } from './dto/create-cat.dto'; @Controller('cats') export class CatsController { constructor(private readonly catsService: CatsService) {} @Post() async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); } @Get() async findAll(): Promise<Cat[]> { return this.catsService.findAll(); } }
這就不得不提到 TypeScript 的優勢了,TypeScript 支持編譯時自動添加一些 metadata 數據:
比如這段代碼:
import "reflect-metadata"; class Guang { @Reflect.metadata("名字", "光光") public say(a: number): string { return '加油鴨'; } }
按理說我們只添加了一個元數據,生成的代碼也確實是這樣的:
但是呢,ts 有一個編譯選項叫做 emitDecoratorMetadata,開啟它就會自動添加一些元數據。
開啟之后再試一下:
你會看到多了三個元數據:
design:type 是 Function,很明顯,這個是描述裝飾目標的元數據,這里裝飾的是函數
design:paramtypes 是 [Number],很容易理解,就是參數的類型
design:returntype 是 String,也很容易理解,就是返回值的類型
所以說,只要開啟了這個編譯選項,ts 生成的代碼會自動添加一些元數據。
然后創建對象的時候就可以通過 design:paramtypes 來拿到構造器參數的類型了,那不就知道怎么注入依賴了么?
所以,nest 源碼里你會看到這樣的代碼:
就是獲取構造器的參數類型的。這個常量就是我們上面說的那個:
這也是為什么 nest 會用 ts 來寫,因為它很依賴這個 emitDecoratorMetadata 的編譯選項。
你用 cli 生成的代碼模版里也都默認開啟了這個編譯選項:
這就是 nest 的核心實現原理:通過裝飾器給 class 或者對象添加 metadata,并且開啟 ts 的 emitDecoratorMetadata 來自動添加類型相關的 metadata,然后運行的時候通過這些元數據來實現依賴的掃描,對象的創建等等功能。
感謝各位的閱讀,以上就是“reflect metadata Nest實現原理是什么”的內容了,經過本文的學習后,相信大家對reflect metadata Nest實現原理是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。