NestJS 入门(2):依赖注入到底解决了什么问题?
上一篇NestJS 入门先搞懂 Module、Controller、Service 讲了 Module / Controller / Service 三件套。学完之后很多人会卡在同一个问题上Controller 里明明没有new AuthService()为什么还能直接用this.authService答案就是依赖注入Dependency Injection简称 DI。这篇文章只讲清楚一件事Nest 如何帮你创建对象、并把它们接到一起。1. 先看「不用 DI」会怎样如果手写 Express 风格登录接口大概会变成这样// 伪代码自己 new 的世界constjwtServicenewJwtService({secret:...});constprismanewPrismaService();constauthServicenewAuthService(jwtService,prisma);constauthControllernewAuthController(authService);问题很快出现谁负责创建谁依赖一多手动组装会变成一棵大树。换实现很难。测试时想换成假的JwtService到处都要改new。容易重复创建。多个地方各自new PrismaService()数据库连接、配置就可能不一致。Nest 的做法是你只声明「我需要什么」创建和接线交给框架。2. 最常见写法构造函数注入认证 Controller 是这样写的Controller(api/auth)exportclassAuthController{constructor(privatereadonlyauthService:AuthService){}Post(login)login(Body()body:{email:string;password:string}){returnthis.authService.login(body.email,body.password);}}关键就一行constructor(privatereadonlyauthService:AuthService){}含义是「创建AuthController时请给我一个AuthService实例。」你不用关心AuthService什么时候被new它内部还依赖了谁全项目是不是共用同一个实例默认情况下Nest Provider 是单例Service 自己也可以继续注入依赖。认证 Service 里同时需要 JWT 能力和数据库能力Injectable()exportclassAuthService{constructor(privatereadonlyjwtService:JwtService,privatereadonlyprisma:PrismaService){}privateasyncgenerateTokens(user:User){constpayload{sub:user.id,email:user.email,name:user.name};constaccessTokenthis.jwtService.sign(payload,{expiresIn:30m,});constrefreshTokenthis.jwtService.sign(payload,{expiresIn:7d,});return{accessToken,refreshToken};}}于是依赖链变成AuthController └── 需要 AuthService ├── 需要 JwtService └── 需要 PrismaService你只在构造函数里「点名」Nest 负责把整条链拼好。3.Injectable()告诉 Nest「我可以被注入」Service、Guard、Strategy 这些类通常会加上Injectable()exportclassAuthService{/* ... */}Injectable()exportclassJwtAuthGuardextendsAuthGuard(jwt){}Injectable()exportclassJwtStrategyextendsPassportStrategy(Strategy){// ...}可以把它理解成一张标签这个类不是普通工具函数它是可以被容器管理、可以被注入的 Provider。没有这张标签、又没在 Module 里正确注册时Nest 往往就不知道该怎么创建它。4. Module 才是「注册表」光写constructor(private readonly authService: AuthService)还不够。Nest 还要在某个 Module 里看到这个类已经登记过了。Module({imports:[PrismaModule,PassportModule,JwtModule.register({secret:process.env.JWT_SECRET||your-secret-key,signOptions:{expiresIn:30m},}),],controllers:[AuthController],providers:[AuthService,JwtStrategy,JwtAuthGuard],exports:[AuthService,JwtAuthGuard],})exportclassAuthModule{}把这四块对应到 DI字段和依赖注入的关系providers本模块「能提供」哪些可注入对象controllers也由 Nest 创建构造函数里的依赖从容器取imports引入别的模块已经 export 出来的能力exports把本模块的 Provider 分享给其他模块所以 DI 不是魔法流程其实很明确在 Module 里注册 Provider在构造函数里声明类型依赖Nest 启动时解析依赖图并创建实例请求进来时直接用已经接好线的对象5. 跨模块共享exportsimports文档模块要用到项目相关能力时不是直接去new ProjectsService()而是Module({imports:[PrismaModule,ProjectsModule],controllers:[DocumentsController],providers:[DocumentsService],exports:[DocumentsService],})exportclassDocumentsModule{}DocumentsService构造函数里注入别人的 ServiceInjectable()exportclassDocumentsService{constructor(privatereadonlyprojectsService:ProjectsService,privatereadonlyprisma:PrismaService){}}规则很简单A 模块要用 B 模块的 ServiceB 必须先exports它A 再importsB 模块少任何一步运行时都会报「Nest can’t resolve dependencies」这类错误。初学时遇到这个报错优先检查有没有注册、有没有 export、有没有 import。6. 全局模块减少到处imports数据库这类「几乎处处都要用」的能力可以做成全局模块import{Global,Module}fromnestjs/common;import{PrismaService}from./prisma.service;Global()Module({providers:[PrismaService],exports:[PrismaService],})exportclassPrismaModule{}Global()的含义是只要根模块引入一次其他模块通常不必再重复imports: [PrismaModule]也能注入PrismaService。适合基础设施数据库、日志、配置。业务模块不建议随便全局化否则模块边界会变糊。7. 一张图看懂「谁创建了谁」以登录接口为例启动阶段Nest 容器装配 AuthModule 注册 AuthController / AuthService / JwtAuthGuard ... JwtModule 提供 JwtService PrismaModule 提供 PrismaService Nest 创建 PrismaService JwtService AuthService(jwtService, prisma) AuthController(authService) 请求阶段 POST /api/auth/login → AuthController.login() → AuthService.login() → JwtService.sign() 签发 token对比手动new你只声明依赖关系创建顺序、单例管理、跨模块共享由容器处理这就是 DI 的核心价值把「对象怎么创建」从业务代码里拿走。8. 为什么测试也更轻松直觉版有了构造函数注入测试时可以塞假依赖而不用改业务代码结构// 伪代码测试时替换依赖constfakeJwt{sign:()fake-token,verify:()({sub:1})};constauthServicenewAuthService(fakeJwtasany,fakePrismaasany);真正项目里还会用 Nest 的测试工具去覆盖整个 Module。入门阶段先记住结论即可依赖从外面传入而不是在类内部写死new替换起来才容易。9. 进阶一瞥循环依赖真实项目里有时会出现DocumentsService依赖ProjectsServiceProjectsService又依赖DocumentsService两边互相要对方Nest 解析依赖图时会卡住。这时会看到forwardRef()imports:[forwardRef(()ProjectsModule)]以及构造函数里的Inject(forwardRef(()DocumentsService))privatereadonlydocumentsService:DocumentsService入门阶段不必深挖。先会用「单向依赖 exports/imports」就够了真遇到循环依赖再回头查forwardRef。10. 小结依赖注入解决的是谁来创建对象、如何把依赖接上。最常用写法constructor(private readonly xxx: XxxService)Injectable()标记可注入类Module 的providers/imports/exports决定能否被解析跨模块共享靠exportsimports基础设施可用Global()报错「can’t resolve dependencies」时先查注册与导出再查依赖方向对照上一篇的三问现在可以多问第四句哪个 Controller 接请求哪个 Service 做业务哪个 Module 组装它们这个 Service 的依赖是从哪里注入进来的下一篇可以讲Guard 如何挡住未登录请求——把UseGuards(JwtAuthGuard)从「装饰器」讲成「请求链路里的一道门」。系列导航上一篇NestJS 入门先搞懂 Module、Controller、Service

相关新闻

OpenUtau:如何用开源虚拟歌手软件创作专业级音乐作品?[特殊字符]

OpenUtau:如何用开源虚拟歌手软件创作专业级音乐作品?[特殊字符]

OpenUtau:如何用开源虚拟歌手软件创作专业级音乐作品?🎵 【免费下载链接】OpenUtau Open singing synthesis platform / Open source UTAU successor 项目地址: https://gitcode.com/gh_mirrors/op/OpenUtau 你是否曾梦想创作属于自己…

2026/9/30 6:41:48 阅读更多 →
机器学习工程化与可复现实验流程设计:升级前先做这几项确认

机器学习工程化与可复现实验流程设计:升级前先做这几项确认

机器学习工程化与可复现实验流程设计:升级前先做这几项确认范围说明: 本文的升级路径为演练;可复现性应由依赖锁定、数据版本和运行记录证明。锁定 Python 包版本并不能保证运行环境完全一致:驱动、CUDA runtime、C ABI 和自定义扩…

2026/10/15 6:18:32 阅读更多 →
MySQL外键约束详解:原理、应用与优化

MySQL外键约束详解:原理、应用与优化

1. 外键基础概念与核心价值外键(Foreign Key)是关系型数据库中实现表间关联的核心机制。作为从业15年的DBA,我处理过上千个外键相关的案例,深刻理解它在数据完整性维护中的不可替代性。简单来说,外键就是一个表中的字段…

2026/10/14 3:57:41 阅读更多 →

最新新闻

3D数字讲师+AI答疑:把静态PPT变成能讲课的智能助教

3D数字讲师+AI答疑:把静态PPT变成能讲课的智能助教

1. 从一份静态课件到一位会讲课的助教:这个项目到底在解决什么如果你做过企业培训、在线课程或者高校里的混合式教学,大概率经历过这样的场景:一份精心打磨的PPT,讲完一遍之后,学员的问题五花八门,而你不可…

2026/10/16 13:42:23 阅读更多 →
C语言typedef实战指南:类型安全与工业级编码规范

C语言typedef实战指南:类型安全与工业级编码规范

1. 为什么我坚持在每个C项目里用typedef,而不是直接写struct、enum或函数指针?刚入行那会儿,我在某嵌入式团队接手一个老项目,代码里满屏都是这样的写法:struct sensor_data {int id;float temperature;uint32_t times…

2026/10/16 13:42:23 阅读更多 →
Attero网络损伤仪实战:故障注入、配置与排坑指南

Attero网络损伤仪实战:故障注入、配置与排坑指南

简介:这份资源是Attero网络损伤仪的中文使用手册,面向网络性能测试工程师、网络运维与设备评估人员,用于快速掌握该仪器的接口布局、软件安装与损伤模型配置方法。手册基于实际使用流程编写,覆盖从PC控制终端连接、.NET Framework…

2026/10/16 13:42:23 阅读更多 →
金融服务平台系统建设实战:从需求拆解到线上排查

金融服务平台系统建设实战:从需求拆解到线上排查

金融科技项目这行干了十来年,让我感受最深的一句话是:金融服务本质上是信任生意,技术只是把信任做成产品的手段。一个挂着 financial-services 名字的项目,往往意味着背后那套账务、风控、合规、对账体系在同时转,任何…

2026/10/16 13:42:23 阅读更多 →
Python垃圾回收机制深度解析:从内存泄漏到性能优化

Python垃圾回收机制深度解析:从内存泄漏到性能优化

1. 为什么你写的Python程序总在某个时刻“突然变慢”——从一次内存泄漏排查说起我去年帮某高校实验室优化一个图像处理脚本,它原本跑500张图要12分钟,但随着图片数量增加,耗时不是线性增长,而是指数级飙升:1000张要38…

2026/10/16 13:42:23 阅读更多 →
802.3-2022以太网标准实操指南:25G/50G/100G参数查询与常见坑

802.3-2022以太网标准实操指南:25G/50G/100G参数查询与常见坑

简介:IEEE 802.3-2022 是以太网领域最新发布的正式标准,面向网络工程师、硬件开发与通信研究人员,提供从 1Mb/s 到 400Gb/s 的完整规范;涵盖介质访问控制协议、半双工/全双工操作、多种物理层接口与背板以太网内容,并刷…

2026/10/16 13:41:21 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →