前言
上篇文章我們學習瞭如何使用nest-cli
來快速生成一個NestJS
後端項目,當我們打開編輯器查看代碼時,會發現整個代碼風格有點類似JAVA的spring
框架,並且你會發現一些service
類在controller
控制器的constructor
中注入後,可以不需要手動new
就可以直接使用該類對應的實例方法。
比如:
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get('/hello')
get(): string {
return this.appService.getHello();
}
}
這其實就是Nest
依賴注入與控制反轉,目的主要是方便代碼之間的解耦從而減少維護成本。
什麼是依賴注入(DI)與控制反轉(IOC)
這兩個概念不要搞混了,IOC其實是面向對象編程中的一種設計模式,而DI則是爲了實現IOC的一種技術。
傳統耦合代碼
比如,我們有兩個類,它們之間存在耦合關係,我們一般會這樣寫:
// A.ts
class A {
name: string
constructor(name: string) {
this.name = name
}
}
// B.ts
class B {
age: number
name: A
constructor(age: number) {
this.age = age
this.name = new A('南玖')
}
}
// main.ts
const b = new B(18)
console.log(b)
當我們遇到類與類之間存在依賴關係時,一般會直接在類的內部創建依賴對象,這樣就會導致各個類之間形成耦合,並且這種關係會隨着依賴關係越來越複雜從而耦合度也會越來越高,最終造成代碼的難以維護。
簡易版IOC
爲了解決上面代碼帶來的耦合性問題,我們可以使用IOC容器來進行管理
// container.ts
export class Container {
modules = new Map()
// 註冊實例
provide(key: string, clazz: any, argvs: Array<any>) {
this.modules.set(key, {clazz, argvs})
}
// 獲取實例
get(key: string) {
const {clazz, argvs} = this.modules.get(key)
return Reflect.construct(clazz, argvs)
}
}
這裏的Reflect.construct
是爲了幫我們實例化一個對象。
以上就是容器化思路,統一管理,可以實現類與類之間的解耦。
Nest JS的IOC與DI
依賴注入是一種控制反轉
IOC(inversion of control)
技術,就是你可以把對象或依賴的實例化交給IOC
容器去處理,在NestJS
中這個容器就是NestJS
的運行時系統。當需要一個對象實例的時候,我們不需要自己手動new xxxClass()
,只需要在合適的地方對類進行註冊,在需要用到的地方直接注入,容器將爲我們完成new
的動作
在Nest
中使用依賴注入一般有以下三步:
聲明定義
使用@Injectable
裝飾器來聲明一個類,它表示該類可以由Nest
的IOC
容器管理
// app.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello nanjiu';
}
}
聲明在什麼地方使用
這是依賴注入的地方,一般是在類的構造函數constructor
中注入,只有完成注入後纔可以使用
// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get('/hello')
get(): string {
return this.appService.getHello();
}
}
官方把appService
稱爲token
,NestJS
會根據這個token
在容器中找到第1步中聲明的類(這個對應關係將在第三步中進行關聯註冊),從而提供對應的實例,這裏的實例全局唯一,只有1個!在第一次需要該實例的時候,Nest
會new
一個出來,而後會緩存起來,後序如果其它地方也注入了這個依賴,那Nest
會從緩存中拿到之前new
出來的實例供大家使用。
建立注入依賴與容器中類的聯繫
依賴注入後還需要在Module
中進行關聯
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Nest
會根據所有注入的依賴關係生成一個依賴關係圖,就有點類似我們使用import
引入各個模塊時也會生成一個複雜的依賴關係圖。這裏AppController
中依賴了AppService
,如果AppService
中還依賴其它東西也會一併放到Nest
構建的依賴關係圖中,Nest
會從下到上按照依賴順序構建出一整張依賴關係圖保證所有的依賴關係正常運作。