NestJs: Module之间的循环依赖问题总结

简述

在NestJS中,循环依赖指的是两个或多个模块之间相互引用导致的依赖关系。这种情况下,由于模块之间的依赖关系无法解决,应用程序可能会遇到编译错误或运行时错误。

就比如我在开发知识库的过程中就遇到了这个问题

很显然,已经自动识别到circular dependency循环依赖问题,并Use forwarRef() to avoid it(建议我用forwarRef去解决它)

那么问题来了?是怎样的一个过程造成的循环依赖呢?

依赖

循环依赖的形成

如图,userService需要调用authService里面的方法,而authService也需要调用controller里面的方法

auth.service.ts

ts 复制代码
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UserService } from 'src/user/user.service';
// import { UserService } from 'src/user/user.service';

@Injectable()
export class AuthService {

  constructor(
    private readonly userService: UserService,
    private readonly jwtService: JwtService
  ) {}

  async validateUser(email: string, pass: string): Promise<any> {
    const user = await this.userService.findByEmail(email);
    if (user && user.password === pass) {
      const { password, ...result } = user;
      return result;
    }
    return null;
  }

  async login(user: any) {
    const payload = { username: user.email, sub: user.id};
    return this.jwtService.sign(payload) // token
 
  }
}

user.controller.ts

ts 复制代码
import { Controller, Post, Req, UseGuards, UnauthorizedException, HttpStatus, Get, Body, UsePipes } from '@nestjs/common';
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
import { LocalAuthGuard } from 'src/auth/local-auth.guard';
import { Restful } from 'src/common/dto/restful.dto';
import { AuthService } from '../auth/auth.service';
import { UserService } from './user.service';
import { CreateUserDto, PageUserDto, UpdateUserDto } from './user.dto';
import { ValidationPipe } from 'src/common/validate/validate.pipe';

@Controller()
export class UserController {

  constructor(private readonly authService: AuthService, private readonly userService: UserService) { }
  // @UseGuards(LocalAuthGuard)
  @Post('auth/admlogin')
  @UseGuards(LocalAuthGuard) // 默认定位到local.strategy.ts
  async login(@Req() req) {
    if (req.user) {
      console.log(req.user)
      const token = await this.authService.login(req.user)

      return Restful.jsonData({ access_token: token })
    }
  }
}

它们两个存在相互依赖的情况 如果我在auth的module不加forwarRef,就会有如下提示:

这个错误通常是由于循环依赖引起的。当两个或多个模块相互引用时,NestJS无法解析模块之间的依赖关系,从而导致错误。

错误消息中提到的潜在原因是两个可能的情况:

  1. 存在循环依赖:模块之间存在互相引用的依赖关系。这通常是因为模块A依赖于模块B,同时模块B又依赖于模块A。为了解决这个问题,可以使用forwardRef()方法来创建延迟加载的模块引用,以避免循环依赖。
  2. "imports"数组中索引0处的模块未定义:这通常是由于导入错误或导入的模块未正确定义导致的。请检查你的导入语句和模块的类型,并确保模块正确定义和导入。

通俗的表达就是: 两个原因,第一个就是形成了循环依赖,第二个就是这个值本来就是 undefined,

解决方案

所以需要我们在他们的模块导入中使用forwardRef

authModule

ts 复制代码
@Module({
  imports: [
    forwardRef(()=>UserModule), PassportModule, 
    JwtModule.register({
      secret: jwtConstants.secret,
      signOptions: {
        expiresIn: '600000s'
      }
    })
  ],

userModule

ts 复制代码
@Module({
  imports: [TypeOrmModule.forFeature([UserEntity]), forwardRef(()=> AuthModule)],
  controllers: [UserController],
  providers: [
    UserService
  ],
  exports: [UserService]
})

除了 Module 和 Module 之间会循环依赖以外,provider 之间也会。

比如 Service 里可以注入别的 Service,自身也可以用来注入。

所以也会有循环引用。

如下就是一个例子:

user.service.ts:

ts 复制代码
import { Injectable } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class UserService {
  constructor(private readonly authService: AuthService) {}
  
  // ...
}

auth.service.ts:

ts 复制代码
import { Injectable } from '@nestjs/common';
import { UserService } from './user.service';

@Injectable()
export class AuthService {
  constructor(private readonly userService: UserService) {}
  
  // ...
}

在这个例子中,UserService依赖于AuthService,AuthService又依赖于UserService,形成了循环引用。

要解决这个循环引用问题,可以使用forwardRef()方法来创建延迟加载的服务引用,如下所示:

user.service.ts:

ts 复制代码
import { Injectable, forwardRef } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class UserService {
  constructor(@Inject(forwardRef(() => AuthService))
  private readonly authService: AuthService) {}
  
  // ...
}

auth.service.ts:

ts 复制代码
import { Injectable, forwardRef } from '@nestjs/common';
import { UserService } from './user.service';

@Injectable()
export class AuthService {
  constructor(@Inject(forwardRef(() => UserService))
  private readonly userService: UserService) {}
  
  // ...
}

通过在服务的构造函数中使用forwardRef()方法,可以创建一个延迟加载的引用,以解决循环引用问题。这样,即使两个服务相互引用,在编译和运行时也不会出现循环引用的错误。

相关推荐
GuWenyue9 小时前
不用第三方SDK!Vue3原生Fetch实现DeepSeek流式输出,90%前端都会踩的分片解析坑一次性解决
前端·人工智能·llm
独隅9 小时前
前端离线暂停更新策略:Service Worker 与 PWA 实战指南
前端
不如摸鱼去10 小时前
Wot UI 2.3.0 发布:二维码组件来了,Open Wot 与 wot-starter 同步更新
前端·ui·微信小程序·前端框架·uni-app
Cloud_bread10 小时前
从ReAct到自主闭环:Agentic Coding核心执行引擎的技术演进
前端·react.js·前端框架
cidy_9810 小时前
OptMem 使用教程
前端
满栀58510 小时前
原生JS + ECharts 多图表可视化实战:从业务实现到工程化优化
开发语言·javascript·echarts
杉氧10 小时前
用 Compose 挑战交互与动效天花板:ComposeCraftLab 开源实验室全解析
android·前端·kotlin
Canace11 小时前
AI 生成到 90% 突然断了:你的解决方案是?
前端·人工智能
TinssonTai11 小时前
Vite 8 版 Chrome 插件全家桶,popup/options/sidepanel 一次集齐
前端·vue.js
玉鸯11 小时前
让 Agent 面向用户:AG-UI 协议构建 Agent 前端
前端·python·agent