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

相关推荐
weifont2 小时前
聊一聊Electron中Chromium多进程架构
javascript·架构·electron
大得3692 小时前
electron结合vue,直接访问静态文件如何跳转访问路径
javascript·vue.js·electron
水银嘻嘻4 小时前
12 web 自动化之基于关键字+数据驱动-反射自动化框架搭建
运维·前端·自动化
it_remember4 小时前
新建一个reactnative 0.72.0的项目
javascript·react native·react.js
小嘟嚷ovo5 小时前
h5,原生html,echarts关系网实现
前端·html·echarts
十一吖i5 小时前
Vue3项目使用ElDrawer后select方法不生效
前端
只可远观5 小时前
Flutter目录结构介绍、入口、Widget、Center组件、Text组件、MaterialApp组件、Scaffold组件
前端·flutter
周胡杰5 小时前
组件导航 (HMRouter)+flutter项目搭建-混合开发+分栏效果
前端·flutter·华为·harmonyos·鸿蒙·鸿蒙系统
敲代码的小吉米6 小时前
前端上传el-upload、原生input本地文件pdf格式(纯前端预览本地文件不走后端接口)
前端·javascript·pdf·状态模式
是千千千熠啊6 小时前
vue使用Fabric和pdfjs完成合同签章及批注
前端·vue.js