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()方法,可以创建一个延迟加载的引用,以解决循环引用问题。这样,即使两个服务相互引用,在编译和运行时也不会出现循环引用的错误。

相关推荐
徐小夕6 分钟前
JitKnow AI知识库 4.7 发布|多文档推理+拖拽 Agent + 技能库,打造更懂业务的 AI 知识中台
前端·vue.js·github
小poop37 分钟前
从“雷峰塔”到“对象”:JavaScript基础语法入门指南
前端·javascript
ReStock活库39 分钟前
Zephyr异步SPI调用返回后,为什么不能立即复用发送缓冲区?
java·前端·网络
计算机魔术师1 小时前
半年营收163亿,算力债却到7800亿,这账谁算得清
前端
chunyublog1 小时前
HDFS 上传文件名空格问题排障教程
前端·javascript·npm
?abc!1 小时前
(贪心)621. 任务调度器
java·前端·c#
摸鱼仙人~2 小时前
校招帮简历字段匹配架构分析报告
java·前端·架构
陈随易2 小时前
副业搞钱点子之赛事与活动
前端·后端·程序员
电梯界知识分子2 小时前
景德镇老房重新设计阶段规划楼梯与电梯,1.7m×1.6m空间装400kg观光电梯,棕色框架茶色玻璃适配中式装修
大数据·服务器·前端·网络·家用电梯
Shell运维手记2 小时前
Shell 脚本入门课堂讲解
前端·chrome