前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Node.js服务端开发教程 (六):依赖注入补漏篇

Node.js服务端开发教程 (六):依赖注入补漏篇

作者头像
一斤代码
发布2019-11-21 14:53:26
1.4K0
发布2019-11-21 14:53:26
举报
文章被收录于专栏:大前端开发大前端开发

最近在写前面两篇关于依赖注入的文章时,我总是在想用一句怎么的话来简单而朴素的描述依赖注入的概念,让从来没接触过的朋友能比较形象的去理解。想来想去,觉得可以站在依赖注入容器的角度说:

你负责告诉我你需要什么(依赖),我负责给你送来什么(注入)

建议多读几遍上面这句话,再回头去阅读前面两篇文章,我觉得你会有更多的收获。

其实在前两篇文章中,关于NestJS依赖注入功能相关的内容已经介绍的差不多了,如果你掌握了的话,已可以顺利的用于实际的开发工作。今天想给大家介绍的是一些关于依赖注入的零碎遗留内容,在日常开发中也会遇到,但不是那么高频。主要有以下几点:

  • 异步资源提供者
  • 循环依赖问题与解决方式
  • 注入范围

异步资源提供者

顾名思义,其实就是在资源创建的时候,存在异步的环节。比如在创建资源的时候,需要先访问一个后端API来获取一些配置信息,然后根据这些配置信息再做进一步的资源创建。这里的后端API访问就是一个异步的动作,这会导致整个资源创建流程也是异步的了。

在NestJS中,大多数的资源提供者都是只支持同步,比如ValueProvider和ClassProvider,能支持异步的只有FactoryProvider。用法其实挺简单的:

代码语言:javascript
复制
{
  provide: ProductService,
  useFactory: async () => {
    // 调用远程接口获取信息
    const configInfo = await getProductServiceConfig()
    
    // 根据远程返回的数据作进一步实例化
    if (configInfo.category) {
      return new ProductService(configInfo.category)
    } else {
      return new ProductService()
    }
  }
}

如上所示,直接将原先useFactory指定的工厂函数声明成async方式的函数,就可以支持异步的创建流程了。

循环依赖问题与解决方式

所谓的循环依赖,就是指两个类之间存在互相依赖的情况,例如:资源A依赖资源B,资源B也需要依赖A,这种情况下,无论是在创建A还是创建B的时候,其实彼此都还不存在,也就是互相找不到对方来满足依赖,这就会发生错误。

在模块之间或提供者之间的嵌套都可能会出现循环依赖关系。通常情况下,我们在设计的时候应该尽量避免循环依赖,但是总有避免不了的情况,在NestJS中提供了一种称为前向引用(forward referencing)的技术来解析循环依赖项。

例如下面示例代码:

代码语言:javascript
复制
@Injectable()
export class CategoryService {
  constructor(
    @Inject(forwardRef(() => ProductService))
    private readonly productService: ProductService,
) {}
}
代码语言:javascript
复制
@Injectable()
export class ProductService {
  constructor(
    @Inject(forwardRef(() => CategoryService))
    private readonly categoryService: CategoryService,
) {}
}

以上的2个类之间有互相依赖关系,各自需要注入对方。如果未使用代码中NestJS框架提供的forwardRef()工具函数,就会报错提示找不到依赖的资源;而使用后,容器可以正确处理互相使用forwardRef()函数标记过的类。

该工具函数也可作用于2个模块之间,解决模块间的循环依赖:

代码语言:javascript
复制
@Module({
  imports: [forwardRef(() => CategoryModule)],
})
export class ProductModule {}
代码语言:javascript
复制
@Module({
  imports: [forwardRef(() => ProductModule)],
})
export class CategoryModule {}

除了使用上面提到的 forwardRef() 工具函数,NestJS还另外提供了一种可行的方式来解决循环依赖,那就是模块引用(Module Reference)。模块引用解决问题的思路是:不通过容器的自动依赖注入,而由我们自己来控制。

通过在类中注入框架提供的ModuleRef,并在模块初始化的生命周期函数中进行手动查找所需要的资源实例,就能避免自动注入时的尴尬问题:

代码语言:javascript
复制
import { Injectable, OnModuleInit } from '@nestjs/common';
import { ProductService } from './product.service';
import { ModuleRef } from '@nestjs/core';

@Injectable()
export class CategoryService implements OnModuleInit {
    private productService: ProductService;

    // 注入框架提供的ModuleRef实例
    constructor(private readonly moduleRef: ModuleRef) { }

    onModuleInit() {
        //使用 moduleRef 从当前模块中查询 ProductService 资源实例
        this.productService = this.moduleRef.get(ProductService);
    }
}

注入范围

默认情况下,NestJS容器中创建的资源对象都是单例的。受益于Node.js的单进程模型,单例模式在NestJS下的使用是非常安全的,不像其他多线程语言对单例的访问操作会存在线程安全问题。因此,在绝大多数情况下,我们的NestJS程序在资源创建这块,都推荐使用默认的单例方式。

这种方式,其实也代表了资源的生存范围(Scope)。比如单例的话,是在应用启动后就被初始化,一直到应用关闭。

既然有单例方式,那肯定还有其他方式的存在。NestJS提供了3种范围:

  • 单例(SINGLETON)- 应用一启动就被实例化,只有一个对象实例,在整个应用程序范围内被共享
  • 请求(REQUEST)- 针对于每个请求生成一个实例,请求处理结束后销毁
  • 零时(TRANSIENT)- 为每个资源消费者生成一个专用实例

我们可以在类的@Injectable装饰器中指定范围:

代码语言:javascript
复制
import { Injectable, Scope } from '@nestjs/common';

@Injectable({ scope: Scope.REQUEST })
export class MyService {}

也可以在定义资源提供者的地方指定范围:

代码语言:javascript
复制
{
  provide: 'MY_MANAGER',
  useClass: MyManager,
  scope: Scope.TRANSIENT,
}

另外,资源依赖路径上的范围会有层级关系,是一个从底至上的冒泡关系,比如下面这样一个A依赖B,B依赖C的关系中:

代码语言:javascript
复制
AService <- BService <- CService

如果我们指定BService的范围为REQUEST,那么上层的AService也会变成REQUEST的,而下层的CService则仍保持默认的SINGLETON。

如果没有特别的原因,建议不要使用SINGLETON以外的方式,因为其他两种方式多多少少会增加系统消耗,影响到程序的性能。

总结

关于NestJS依赖注入相关的内容已经介绍的差不多了,有了这些基础,相信你可以在这块能比较顺利的开展工作了。如果你在使用的过程中遇到什么问题,可以通过翻阅官方文档了解更多细节。

在后面的一两篇文章内,我将计划再介绍一些关于NestJS框架的其他核心基础,我一直相信,基础打好了,才会让你往后的做事效率达到事半功倍的效果。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 异步资源提供者
  • 循环依赖问题与解决方式
  • 注入范围
  • 总结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档