【NestJS系列】DI依賴注入與IOC控制反轉

前言

上篇文章我們學習瞭如何使用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裝飾器來聲明一個類,它表示該類可以由NestIOC容器管理

// 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稱爲tokenNestJS會根據這個token在容器中找到第1步中聲明的類(這個對應關係將在第三步中進行關聯註冊),從而提供對應的實例,這裏的實例全局唯一,只有1個!在第一次需要該實例的時候,Nestnew一個出來,而後會緩存起來,後序如果其它地方也注入了這個依賴,那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會從下到上按照依賴順序構建出一整張依賴關係圖保證所有的依賴關係正常運作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章