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

相关推荐
桂月二二4 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
CodeClimb5 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
hunter2062066 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb6 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角6 小时前
CSS 颜色
前端·css
九酒6 小时前
从UI稿到代码优化,看Trae AI 编辑器如何帮助开发者提效
前端·trae
浪浪山小白兔7 小时前
HTML5 新表单属性详解
前端·html·html5
lee5767 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579657 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter
光头程序员8 小时前
grid 布局react组件可以循数据自定义渲染某个数据 ,或插入某些数据在某个索引下
javascript·react.js·ecmascript