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

相关新闻

最新新闻

深度学习代码能力提升:从环境搭建到工程化部署的完整实践指南

深度学习代码能力提升:从环境搭建到工程化部署的完整实践指南

刚入门深度学习,代码能力怎么快速提升?这是很多新手的核心痛点。理论看懂了,公式也理解了,但一打开代码编辑器就无从下手,或者写出来的代码效率低下、Bug频出。这篇文章不聊空洞的方法论,直接给你一套可执行…

2026/8/10 2:07:29
钣金折叠工艺:从材料特性到数字化设计的核心技术解析

钣金折叠工艺:从材料特性到数字化设计的核心技术解析

1. 钣金设计中的折叠艺术钣金加工车间里,金属板材在折弯机下发出有节奏的声响,原本平面的板材在几分钟内就变成了结构复杂的立体零件。这种从二维到三维的转变过程,正是现代制造业中最具智慧的工艺之一。作为一名从业十余年的钣金工程师&…

2026/8/10 2:07:29
Vue3项目创建与环境配置全指南

Vue3项目创建与环境配置全指南

1. Vue3 项目创建基础与环境准备作为前端开发者,当你准备开始一个全新的Vue3项目时,首先要确保开发环境配置正确。与Vue2时代不同,Vue3的生态系统已经全面转向现代构建工具链,这带来了更高效的开发体验。1.1 Node.js 版本选择与安…

2026/8/10 2:07:29
Unity HDRP中VAT技术完整实现与优化指南

Unity HDRP中VAT技术完整实现与优化指南

1. 项目概述:为什么要在HDRP里折腾VAT?如果你在Unity里做过一些需要大量动态形变的特效,比如爆炸、流体、布料模拟,或者复杂的角色变形,肯定对性能问题深有体会。传统的骨骼动画或逐顶点CPU计算,在面数一高…

2026/8/10 2:07:29
GRETNA:无需编程!三步完成专业级脑网络分析的全能工具箱

GRETNA:无需编程!三步完成专业级脑网络分析的全能工具箱

GRETNA:无需编程!三步完成专业级脑网络分析的全能工具箱 【免费下载链接】GRETNA A Graph-theoretical Network Analysis Toolkit in MATLAB 项目地址: https://gitcode.com/gh_mirrors/gr/GRETNA 你是否对大脑的复杂连接模式充满好奇&#xff0c…

2026/8/10 2:07:29
Unity性能优化利器:SimpleLOD插件自动化网格简化与LOD生成全解析

Unity性能优化利器:SimpleLOD插件自动化网格简化与LOD生成全解析

1. 项目概述:为什么我们需要SimpleLOD?在Unity3D游戏开发中,性能优化是一个永恒的话题,尤其是场景渲染性能。当你的场景中塞满了从SolidWorks导入的高精度机械模型,或者是从资源商店购买的、面数动辄几十万的精美建筑时…

2026/8/10 2:02:29