写在前面:在nest.js中,底层服务被封装在service里,我们需要在controller中调用它们,但是在此之前为了做控制反转,我们需要先使用module把依赖收集起来并提供给controller,最后在controller中注册(注入)依赖供后续调用。
自定义依赖名称
先来看一个基本的使用语法糖的例子(没有自定义依赖名称):
import { UserService } from './user.service'; @Module({ controllers: [UserController], providers: [UserService] // 依赖在这里提供 })
// 不需要额外import @Controller('user') export class UserController { /* 依赖在这一行注入,由于使用的是语法糖,直接写成userService:UserService的形式就行了,无需@Inject() */ constructor(private readonly userService: UserService) {} // 以下代码不重要不用管 @Get() findAll() { return this.userService.findAll(); } }
但其实,module.ts里的provide的原始写法是这样子的
@Module({ controllers: [UserController], /* 这里的写法变了 */ providers: [ { provide: 'xiaoman', useClass: UserService, }, ], })
在这种写法下,controller.ts里构造器注入的依赖需要多加一个@Inject(<string>)装饰器,字符串的内容就是provider的内容
@Controller('user') export class UserController { constructor(@Inject('xiaoman') private readonly userService: UserService) {} @Get() findAll() { return this.userService.findAll(); } }
以上,我们就完成了自定义依赖名称的过程
自定义注入值
除了自定义依赖外,我们也可以自定义注入值,这在所有服务都需要依赖同一个值的时候非常好用
@Module({ controllers: [UserController], providers: [ { provide:'JD', useValue:['JD','TB'] } ], })
同样的,这个依赖需要被注入到controllers.ts中
@Controller('user') export class UserController { constructor( // 使用Inject()装饰器注入对应的值 @Inject('JD') private shopList: string[], ) {} @Get() findAll() { return this.shopList; } }
工厂模式
如果服务间存在依赖,可以使用工厂模式
import { UserService3 } from './user.service3'; @Module({ controllers: [UserController], providers: [ { provide: 'xiaoman', useClass: UserService, }, { provide: 'Test', // 必须要先inject才能将依赖写进useFactory inject: ["xiaoman"], useFactory(UserService:UserService){ // 在这里将依赖传给UserService3的构造函数 return new UserService3(UserService) } }, ], })
import { Inject, Injectable } from '@nestjs/common'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; @Injectable() export class UserService3 { /* 注入Test依赖 */ constructor(@Inject('Test') private readonly test){} create(createUserDto: CreateUserDto) { return 'This action adds a new user'; } findAll() { // 调用依赖中的方法 return this.test.findAll(); } findOne(id: number) { return `This action returns a #${id} user`; } update(id: number, updateUserDto: UpdateUserDto) { return `This action updates a #${id} user`; } remove(id: number) { return `This action removes a #${id} user`; } }
import { UserService3 } from './user.service3'; @Controller('user') export class UserController { constructor( // 注入Test依赖,这个依赖的本质是注入了UserService服务的UserService3的实例对象 @Inject('Test') private test: UserService3, ) {} @Get() findAll() { return this.test.findAll(); } }
异步工厂模式
useFactory支持异步模式
@Module({ controllers: [UserController], providers: [ // 准备注入Test的依赖 { provide: 'xiaoman', useClass: UserService, }, // 注入controller的依赖 { provide: 'Test', inject: ['xiaoman'], // 此处使用异步逻辑。注意返回的内容是一段字符串,因此没有方法 async useFactory(UserService: UserService) { return await new Promise((r) => { setTimeout(() => { r(UserService.findAll()); },2000); }); }, }, ], })
将依赖注入controller与前端交互
@Controller('user') export class UserController { constructor( @Inject('Test') private test: UserService, ) {} @Get() findAll() { return this.test; } }
以上,当我们刷新页面后,过两秒钟页面才会更新
怎样?是不是感到非常懵逼?没关系,我们来理清一下service、module和controller之间的关系
- Service:提供底层Web服务(类),是方法的实际提供者,拥有装饰器@Injectable
- Module:将服务类从Service中导入(需要import),实例化后将其提供给controller。在IOC体系中充当中间管理者,拥有装饰器@Module
- Controller:一切服务在此调用(不需要import,只要用@Inject(<string>)注入依赖即可使用),前端访问接口返回什么内容全部在此处决定,需要注意的是如果在Module里使用语法糖模式则无需额外@Inject()