夜猫子的知识栈 夜猫子的知识栈
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

夜猫子

前端练习生
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Node基础

  • 《MySQL》学习笔记

  • Midway

  • Nest

    • 开篇词
    • 学习理由
    • nest概念扫盲
    • 快速掌握 nestcli
    • 5种http数据传输方式
    • IoC 解决了什么痛点问题?
    • 如何调试 Nest 项目
    • Provider注入对象
      • 注入声明
        • 在当前模块注入
        • 重导出注入
      • providers
      • useValue 指定值注入
      • useFactory 动态注入
        • 注入对象
        • 注入其他 provider
        • 异步注入
        • 指定注入别名
      • 总结
    • 全局模块和生命周期
    • AOP 架构有什么好处?
    • 一网打尽 Nest 全部装饰器
    • Nest如何自定义装饰器
    • Metadata和Reflector
    • ExecutionContext切换上下文
    • Module和Provider的循环依赖处理
    • 如何创建动态模块
    • Nest和Express,fastify
    • Nest的Middleware
    • RxJS和Interceptor
    • 内置Pipe和自定义Pipe
    • ValidationPipe验证post请求参数
    • 如何自定义 Exception Filter
    • 图解串一串 Nest 核心概念
    • 接口如何实现多版本共存
    • Express如何使用multer实现文件上传
    • Nest使用multer实现文件上传
    • 图书管理系统
    • 大文件分片上传
    • 最完美的 OSS 上传方案
    • Nest里如何打印日志
    • 为什么Node里要用Winston打印日志
    • Nest 集成日志框架 Winston
    • 通过Desktop学Docker也太简单了
    • 你的第一个 Dockerfile
  • 其他

  • 服务端
  • Nest
神说要有光
2025-03-10
目录

Provider注入对象

Nest 实现了 IoC 容器,会从入口模块开始扫描,分析 Module 之间的引用关系,对象之间的依赖关系,自动把 provider 注入到目标对象。

而这个 provider 也有好几种,这节我们就来看一下。

我们创建个 nest 项目:

nest new custom-provider
1

可以看到 AppService 是被 @Injectable 修饰的 class:

# 注入声明

要在 Inject 中使用注入值就必须先 providers 注入。

比如在 personService 中注入 aaaService,并使用 aaaService 中的方法:

image-20250318173724983

有两种实现方式:1.在当前模块注入;2.重导出注入。

# 在当前模块注入

image-20250318173900883

只要在当前模块中 providers,那么当前模块的 controllers 和其他 providers 都能注入 providers 中的内容

即 PersonController 和 PersonService 都能使用

  @Inject(AaaService)
  private readonly aaaService: AaaService;
1
2

笔记

需要特别注意的是 AaaService 和 PersonService 互相引用导致循环依赖需要通过 forwardRef 来解决(创建了一个 延迟引用,允许在运行时解析依赖关系)

::

# 重导出注入

但如果要从导入的 Module 中自动获取改模块中的注入值,就必须 exports 导出注入,这种方式叫做模块的重导出。

image-20250318172909004

image-20250318173938073

父模块可以直接注入子模块中 exports 的内容

笔记

由此可以类推,将对应模块做成全局模块,那么就可以在任意地方注入该模块 exports 的内容

# providers

在 Module 的 providers 里声明:

这就是 provider。

其实这是一种简写,完整的写法是这样的:

{
  provide: AppService,
  useClass: AppService
}
1
2
3
4

通过 provide 指定 token,通过 useClass 指定对象的类,Nest 会自动对它做实例化后用来注入。

在 AppController 的构造器里参数里声明了 AppService 的依赖,就会自动注入:

如果不想用构造器注入,也可以属性注入:

@Inject(AppService)
private readonly appService: AppService;
1
2

通过 @Inject 指定注入的 provider 的 token 即可。

有的同学说,在构造器参数里指定 AppService 的依赖的时候也没指定 token 啊?

那是因为 AppService 这个 class 本身就是 token。

当然,这个 token 也可以是字符串:

{
    provide: 'app_service',
    useClass: AppService
}
1
2
3
4

如果 token 是字符串的话,注入的时候就要用 @Inject 手动指定注入对象的 token 了:

@Inject('app_service') private readonly appService: AppService
1

我们调试下:

点击调试面板的 create launch.json file,创建调试配置文件:

添加这样一个调试配置:

{
    "type": "node",
    "request": "launch",
    "name": "debug nest",
    "runtimeExecutable": "npm",
    "args": [
        "run",
        "start:dev",
    ],
    "skipFiles": [
        "<node_internals>/**"
    ],
    "console": "integratedTerminal",
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在 getHello 方法打个断点,点击调试启动:

浏览器访问 http://localhost:3000 ,代码会在断点处断住。

可以看到,这时候 appService 就有值了。

也就是说,用字符串或者 class 做 token 的 provider,都可以正确被注入到目标对象。

相比之下,用 class 做 token 可以省去 @Inject,比较简便。

# useValue 指定值注入

除了指定 class 外,还可以直接指定一个值,让 IoC 容器来注入。

{
    provide: 'person',
    useValue: {
        name: 'aaa',
        age: 20
    }
}
1
2
3
4
5
6
7

使用 provide 指定 token,使用 useValue 指定值。

然后在对象里注入它:

@Inject('person') private readonly person: {name: string, age: number}
1

调试一下可以看到,确实是注入了:

# useFactory 动态注入

provider 的值可能是动态产生的,Nest 也同样支持:

{
    provide: 'person2',
    useFactory() {
        return {
            name: 'bbb',
            desc: 'cccc'
        }
    }
}
1
2
3
4
5
6
7
8
9

# 注入对象

我们可以使用 useFactory 来动态创建一个对象。

在对象里注入:

@Inject('person2') private readonly person2: {name: string, desc: string}
1

类型是 {name: string, age: number} 。

调试下,也是可以拿到创建出的对象的:

# 注入其他 provider

这个 useFactory 支持通过参数注入别的 provider:

{
  provide: 'person3',
  useFactory(person: { name: string }, appService: AppService) {
    return {
      name: person.name,
      desc: appService.getHello()
    }
  },
  inject: ['person', AppService]
}
1
2
3
4
5
6
7
8
9
10

通过 inject 声明了两个 token,一个是字符串 token 的 person,一个是 class token 的 AppService。

也就是注入这两个 provider:

在 return 那里打个断点。

可以看到,在调用 useFactory 方法的时候,Nest 就会注入这两个对象:

# 异步注入

useFactory 支持异步:

{
  provide: 'person5',
  async useFactory() {
    await new Promise((resolve) => {
      setTimeout(resolve, 3000);
    });
    return {
      name: 'bbb',
      desc: 'cccc'
    }
  },
},
1
2
3
4
5
6
7
8
9
10
11
12

Nest 会等拿到异步方法的结果之后再注入:

这样就可以更灵活的创建注入对象。

# 指定注入别名

此外,provider 还可以通过 useExisting 来指定别名:

{
  provide: 'person4',
  useExisting: 'person2'
}
1
2
3
4

这里就是给 person2 的 token 的 provider 起个新的 token 叫做 person4。

然后就可以用新 token 来注入了:

这些自定义 provider 的方式里,最常用的是 useClass,不过我们一般会用简写,也就是直接指定 class。

useClass 的方式由 IoC 容器负责实例化,我们也可以用 useValue、useFactory 直接指定对象。

useExisting 只是用来起别名的,有的场景下会用到。

比如 @nestjs/typeorm 里就用到了 useValue、useFactory、useExisting:

它用 useValue 来注入一段字符串 (opens new window):

用 useFactory 根据传入的 options 动态创建数据库连接对象 (opens new window):

用 useExisting 给 DataSource 起了一个 Connection 的别名 (opens new window):

这里是一个版本用了 Connection,一个版本用了 DataSource,通过 useExisting 起别名就可以兼容两者。

此外,如果觉得构造器注入写起来不方便,可以使用属性注入,效果一样:

案例代码在小册仓库 (opens new window)。

# 总结

一般情况下,provider 是通过 @Injectable 声明,然后在 @Module 的 providers 数组里注册的 class。

默认的 token 就是 class,这样不用使用 @Inject 来指定注入的 token。

但也可以用字符串类型的 token,不过注入的时候要用 @Inject 单独指定。

除了可以用 useClass 指定注入的 class,还可以用 useValue 直接指定注入的对象。

如果想动态生成对象,可以使用 useFactory,它的参数也注入 IOC 容器中的对象,然后动态返回 provider 的对象。

如果想起别名,可以用 useExisting 给已有的 token,指定一个新 token。

灵活运用这些 provider 类型,就可以利用 Nest 的 IOC 容器中注入任何对象。

编辑 (opens new window)
上次更新: 2025/5/14 16:47:16
如何调试 Nest 项目
全局模块和生命周期

← 如何调试 Nest 项目 全局模块和生命周期→

最近更新
01
IoC 解决了什么痛点问题?
03-10
02
如何调试 Nest 项目
03-10
03
全局模块和生命周期
03-10
更多文章>
Copyright © 2019-2025 Study | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式