简述
在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无法解析模块之间的依赖关系,从而导致错误。
错误消息中提到的潜在原因是两个可能的情况:
- 存在循环依赖:模块之间存在互相引用的依赖关系。这通常是因为模块A依赖于模块B,同时模块B又依赖于模块A。为了解决这个问题,可以使用
forwardRef()
方法来创建延迟加载的模块引用,以避免循环依赖。 - "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()
方法,可以创建一个延迟加载的引用,以解决循环引用问题。这样,即使两个服务相互引用,在编译和运行时也不会出现循环引用的错误。