偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Nest 的實(shí)現(xiàn)原理?理解了 Reflect Metadata 就懂了

開(kāi)發(fā) 前端
Nest 是 Node.js 的后端框架,他的核心就是 IOC 容器,也就是自動(dòng)掃描依賴(lài),創(chuàng)建實(shí)例對(duì)象并且自動(dòng)依賴(lài)注入。

Nest 是 Node.js 的服務(wù)端框架,它最出名的就是 IOC(inverse of control) 機(jī)制了,也就是不需要手動(dòng)創(chuàng)建實(shí)例,框架會(huì)自動(dòng)掃描需要加載的類(lèi),并創(chuàng)建他們的實(shí)例放到容器里,實(shí)例化時(shí)還會(huì)根據(jù)該類(lèi)的構(gòu)造器參數(shù)自動(dòng)注入依賴(lài)。

它一般是這樣用的:

比如入口 Module 里引入某個(gè)模塊的 Module:

import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule {}

然后這個(gè)模塊的 Module 里會(huì)聲明 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 對(duì)應(yīng)的處理邏輯:

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();
}
}

這個(gè) CatsController 的構(gòu)造器聲明了對(duì) CatsService 的依賴(lài):

然后 CatsService 里就可以去操作數(shù)據(jù)庫(kù)進(jìn)行增刪改查了。這里簡(jiǎn)單實(shí)現(xiàn)一下:

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;
}
}

之后在入口處調(diào)用 create 把整個(gè) nest 應(yīng)用跑起來(lái):

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();

然后瀏覽器訪問(wèn)下我們寫(xiě)的那個(gè) controller 對(duì)應(yīng)的 url,打個(gè)斷點(diǎn):

圖片圖片

你會(huì)發(fā)現(xiàn) controller 的實(shí)例已經(jīng)創(chuàng)建了,而且 service 也給注入了。這就是依賴(lài)注入的含義。

這種機(jī)制就叫做 IOC(控制反轉(zhuǎn)),也叫依賴(lài)注入,好處是顯而易見(jiàn)的,就是只需要聲明依賴(lài)關(guān)系,不需要自己創(chuàng)建對(duì)象,框架會(huì)掃描聲明然后自動(dòng)創(chuàng)建并注入依賴(lài)。

Java 里最流行的 Spring 框架就是 IOC 的實(shí)現(xiàn),而 Nest 也是這樣一個(gè)實(shí)現(xiàn)了 IOC 機(jī)制的 Node.js 的后端框架。

不知道大家有沒(méi)有感覺(jué)很神奇,只是通過(guò)裝飾器聲明了一下,然后啟動(dòng) Nest 應(yīng)用,這時(shí)候?qū)ο缶徒o創(chuàng)建好了,依賴(lài)也給注入了。

那它是怎么實(shí)現(xiàn)的呢?

大家如果就這樣去思考它的實(shí)現(xiàn)原理,還真不一定能想出來(lái),因?yàn)槿鄙倭艘恍┣爸弥R(shí)。也就是實(shí)現(xiàn) Nest 最核心的一些 api:Reflect 的 metadata 的 api。

Reflect Metadata

有的同學(xué)會(huì)說(shuō),Reflect 的 api 我很熟呀,就是操作對(duì)象的屬性、方法、構(gòu)造器的一些 api:

比如 Reflect.get 是獲取對(duì)象屬性值。

圖片圖片

Reflect.set 是設(shè)置對(duì)象屬性值。

圖片圖片

Reflect.has 是判斷對(duì)象屬性是否存在

圖片圖片

Reflect.apply 是調(diào)用某個(gè)方法,傳入對(duì)象和參數(shù)。

圖片

Reflect.construct 是用構(gòu)造器創(chuàng)建對(duì)象實(shí)例,傳入構(gòu)造器參數(shù)。

圖片

這些 api 在 MDN 文檔里可以查到,因?yàn)樗鼈兌家呀?jīng)是 es 標(biāo)準(zhǔn)了,也被很多瀏覽器實(shí)現(xiàn)了。

但是實(shí)現(xiàn) Nest 用到的 api 還沒(méi)有進(jìn)入標(biāo)準(zhǔn),還在草案階段,也就是 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 分別用于設(shè)置和獲取某個(gè)類(lèi)的元數(shù)據(jù),如果最后傳入了屬性名,還可以單獨(dú)為某個(gè)屬性設(shè)置元數(shù)據(jù)。

那元數(shù)據(jù)存在哪呢?

存在類(lèi)或者對(duì)象上呀,如果給類(lèi)或者類(lèi)的靜態(tài)屬性添加元數(shù)據(jù),那就保存在類(lèi)上,如果給實(shí)例屬性添加元數(shù)據(jù),那就保存在對(duì)象上,用類(lèi)似 [[metadata]] 的 key 來(lái)存的。

這有啥用呢?

看上面的 api 確實(shí)看不出啥來(lái),但它也支持裝飾器的方式使用:

@Reflect.metadata(metadataKey, metadataValue)
class C {
@Reflect.metadata(metadataKey, metadataValue)
method() {
}
}

Reflect.metadata 裝飾器當(dāng)然也可以再封裝一層:

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;
}
}

然后我們就可以通過(guò) Reflect metadata 的 api 或者這個(gè)類(lèi)和對(duì)象的元數(shù)據(jù)了:

let obj = new Guang("a", 1);
let paramTypes = Reflect.getMetadata("design:paramtypes", inst, "add");
// [Number, Number]

這里我們用 Reflect.getMetadata 的 api 取出了 add 方法的參數(shù)的類(lèi)型。

看到這里,大家是否明白 nest 的原理了呢?

我們?cè)倏聪?nest 的源碼:

圖片圖片

上面就是 @Module 裝飾器的實(shí)現(xiàn),里面就調(diào)用了 Reflect.defineMetadata 來(lái)給這個(gè)類(lèi)添加了一些元數(shù)據(jù)。

所以我們這樣用的時(shí)候:

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}

其實(shí)就是給 CatsModule 添加了 controllers 的元數(shù)據(jù)和 providers 的元數(shù)據(jù)。

后面創(chuàng)建 IOC 容器的時(shí)候就會(huì)取出這些元數(shù)據(jù)來(lái)處理:

圖片圖片

而且 @Controller 和 @Injectable 的裝飾器也是這樣實(shí)現(xiàn)的:

圖片圖片

看到這里,大家是否想明白 nest 的實(shí)現(xiàn)原理了呢?

其實(shí)就是通過(guò)裝飾器給 class 或者對(duì)象添加元數(shù)據(jù),然后初始化的時(shí)候取出這些元數(shù)據(jù),進(jìn)行依賴(lài)的分析,然后創(chuàng)建對(duì)應(yīng)的實(shí)例對(duì)象就可以了。

所以說(shuō),nest 實(shí)現(xiàn)的核心就是 Reflect metadata 的 api。

當(dāng)然,現(xiàn)在 metadata 的 api 還在草案階段,需要使用 reflect-metadata 這個(gè) polyfill 包才行。

其實(shí)還有一個(gè)疑問(wèn),依賴(lài)的掃描可以通過(guò) metadata 數(shù)據(jù),但是創(chuàng)建的對(duì)象需要知道構(gòu)造器的參數(shù),現(xiàn)在并沒(méi)有添加這部分 metadata 數(shù)據(jù)呀:

比如這個(gè) CatsController 依賴(lài)了 CatsService,但是并沒(méi)有添加 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 的優(yōu)勢(shì)了,TypeScript 支持編譯時(shí)自動(dòng)添加一些 metadata 數(shù)據(jù):

比如這段代碼:

import "reflect-metadata";
class Guang {
@Reflect.metadata("名字", "光光")
public say(a: number): string {
return '加油鴨';
}
}

按理說(shuō)我們只添加了一個(gè)元數(shù)據(jù),生成的代碼也確實(shí)是這樣的:

圖片

但是呢,ts 有一個(gè)編譯選項(xiàng)叫做 emitDecoratorMetadata,開(kāi)啟它就會(huì)自動(dòng)添加一些元數(shù)據(jù)。

開(kāi)啟之后再試一下:

圖片圖片

你會(huì)看到多了三個(gè)元數(shù)據(jù):

design:type 是 Function,很明顯,這個(gè)是描述裝飾目標(biāo)的元數(shù)據(jù),這里裝飾的是函數(shù)

design:paramtypes 是 [Number],很容易理解,就是參數(shù)的類(lèi)型

design:returntype 是 String,也很容易理解,就是返回值的類(lèi)型

所以說(shuō),只要開(kāi)啟了這個(gè)編譯選項(xiàng),ts 生成的代碼會(huì)自動(dòng)添加一些元數(shù)據(jù)。

然后創(chuàng)建對(duì)象的時(shí)候就可以通過(guò) design:paramtypes 來(lái)拿到構(gòu)造器參數(shù)的類(lèi)型了,那不就知道怎么注入依賴(lài)了么?

所以,nest 源碼里你會(huì)看到這樣的代碼:

圖片

就是獲取構(gòu)造器的參數(shù)類(lèi)型的。這個(gè)常量就是我們上面說(shuō)的那個(gè):

圖片

這也是為什么 nest 會(huì)用 ts 來(lái)寫(xiě),因?yàn)樗芤蕾?lài)這個(gè) emitDecoratorMetadata 的編譯選項(xiàng)。

你用 cli 生成的代碼模版里也都默認(rèn)開(kāi)啟了這個(gè)編譯選項(xiàng):

圖片

這就是 nest 的核心實(shí)現(xiàn)原理:通過(guò)裝飾器給 class 或者對(duì)象添加 metadata,并且開(kāi)啟 ts 的 emitDecoratorMetadata 來(lái)自動(dòng)添加類(lèi)型相關(guān)的 metadata,然后運(yùn)行的時(shí)候通過(guò)這些元數(shù)據(jù)來(lái)實(shí)現(xiàn)依賴(lài)的掃描,對(duì)象的創(chuàng)建等等功能。

總結(jié)

Nest 是 Node.js 的后端框架,他的核心就是 IOC 容器,也就是自動(dòng)掃描依賴(lài),創(chuàng)建實(shí)例對(duì)象并且自動(dòng)依賴(lài)注入。

要搞懂它的實(shí)現(xiàn)原理,需要先學(xué)習(xí) Reflect metadata 的 api:

這個(gè)是給類(lèi)或者對(duì)象添加 metadata 的??梢酝ㄟ^(guò) Reflect.metadata 給類(lèi)或者對(duì)象添加元數(shù)據(jù),之后用到這個(gè)類(lèi)或者對(duì)象的時(shí)候,可以通過(guò) Reflect.getMetadata 把它們?nèi)〕鰜?lái)。

Nest 的 Controller、Module、Service 等等所有的裝飾器都是通過(guò) Reflect.meatdata 給類(lèi)或?qū)ο筇砑釉獢?shù)據(jù)的,然后初始化的時(shí)候取出來(lái)做依賴(lài)的掃描,實(shí)例化后放到 IOC 容器里。

實(shí)例化對(duì)象還需要構(gòu)造器參數(shù)的類(lèi)型,這個(gè)開(kāi)啟 ts 的 emitDecoratorMetadata 的編譯選項(xiàng)之后, ts 就會(huì)自動(dòng)添加一些元數(shù)據(jù),也就是 design:type、design:paramtypes、design:returntype 這三個(gè),分別代表被裝飾的目標(biāo)的類(lèi)型、參數(shù)的類(lèi)型、返回值的類(lèi)型。

當(dāng)然,reflect metadata 的 api 還在草案階段,需要引入 refelect metadata 的包做 polyfill。

nest 的一系列裝飾器就是給 class 和對(duì)象添加 metadata 的,然后依賴(lài)掃描和依賴(lài)注入的時(shí)候就把 metadata 取出來(lái)做一些處理。

理解了 metadata,nest 的實(shí)現(xiàn)原理就很容易搞懂了。

責(zé)任編輯:姜華 來(lái)源: 神光的編程秘籍
相關(guān)推薦

2022-05-03 00:03:11

狀態(tài)管理前端開(kāi)發(fā)

2019-05-28 09:40:39

TCP協(xié)議socket接口

2019-05-17 09:02:19

TCP協(xié)議服務(wù)端

2020-04-16 10:55:03

Java虛擬機(jī)字節(jié)碼

2019-07-02 05:02:56

NVMe接口存儲(chǔ)

2019-12-26 09:15:44

網(wǎng)絡(luò)IOLinux

2024-03-15 08:23:26

異步編程函數(shù)

2023-12-01 08:39:29

分布式鎖系統(tǒng)

2020-08-10 15:24:05

Snowflake算法開(kāi)源

2017-09-19 19:07:00

ZStack混合云災(zāi)備

2024-11-25 07:39:48

2022-10-20 18:43:32

C語(yǔ)言golang安全

2018-03-05 11:29:17

云計(jì)算云服務(wù)服務(wù)器

2022-07-06 08:30:36

vuereactvdom

2012-11-30 11:19:02

JavaScript

2018-03-21 16:19:40

MVCMVPMVVM

2021-12-13 10:43:45

HashMapJava集合容器

2010-08-29 21:09:57

DHCP協(xié)議

2021-10-15 13:47:19

覆蓋率檢測(cè) istanbul 總代碼的比例

2020-04-27 07:28:00

Java反射動(dòng)態(tài)代理
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)