神光《Nest 通关秘籍》学习总结-理解IOC机制
最近在学习神光大神的《Nest通关秘籍》,该小册主要包含下面这些内容:
想购买的可以点击《传送门》。
接下来的日子里,我将更新一系列的学习笔记。感兴趣的可以关注我的专栏《Nest 通关秘籍》学习总结。
特别申明:本系列文章已经经过作者本人的允许。 大家也不要想着白嫖,我的笔记只是个人边学习边记录的,不是很完整,大家想要深入学习还是要自己去购买原版小册。
本章我们来学习nest中的IOC机制。
在Nest项目中,我们可以看到很多这样奇奇怪怪的写法:
这样的:
这样的:
还有这样的:
乍一看,这些都是啥东西啊,好像跟我们平时写的前端不太一样啊。它的controller
、service
、module
它们都是class对象,都没有new呢,它们怎么调用的呢?它为什么可以这么写呢?
要想搞懂它们,首先你得知道什么是IOC(Inverse Of Control)。
老规矩,先搞个项目:
nest new nest-app-ioc
先介绍一下在没有IOC之前我们创建对象都是这样的:
const config = new Config({ username: 'xxx', password: 'xxx'});
const dataSource = new DataSource(config);
const repository = new Repository(dataSource);
const service = new Service(repository);
const controller = new Controller(service);
要经过一系列的初始化之后才可以使用 Controller 对象。在应用初始化的时候,需要理清依赖的先后关系,创建一大堆对象组合起来,还要保证不要多次 new,是不是很麻烦?这个问题是每一个后端系统都有的痛点问题。
而IOC就是解决这个痛点的方式。那么它是如何解决的呢?
之前我们手动创建和组装对象不是很麻烦么,我能不能在 class 上声明依赖了啥,然后让工具去分析我声明的依赖关系,根据先后顺序自动把对象创建好了,然后组装起来呢?
我们把IOC想像成一个容器,程序初始化的时候会扫描 class 上声明的依赖关系,然后把这些 class 都给 new 一个实例放到容器里。创建对象的时候,还会把它们依赖的对象注入进去。这样不就完成了自动的对象创建和组装么?这种依赖注入的方式叫做 Dependency Injection,简称 DI。本来是手动 new 依赖对象,然后组装起来,现在是声明依赖了啥,等待被注入。
从主动创建依赖到被动等待依赖注入,这就是 Inverse Of Control,反转控制。
在 class 上声明依赖的方式,大家都选择了装饰器的方式(在 java 里这种语法叫做注解)。
下面我们在实际项目中来看看Nest是这么利用IOC的。
前面我们已经创建好了项目。我们看看代码里它是怎么创建对象的:
在app.service.ts
中:
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
它有一个 AppService 声明了 @Injectable,代表这个 class 可注入,那么 nest 就会把它的对象放到 IOC 容器里。
紧接着,在app.controller.ts
中:
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
AppController 声明了 @Controller,代表这个 class 可以被注入,nest 也会把它放到 IOC 容器里。
然后在 AppModule 里引入:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
通过 @Module 声明模块,其中 controllers 是控制器,只能被注入。
providers 里可以被注入,也可以注入别的对象,比如这里的 AppService。
然后在入口模块main.ts
里跑起来:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
当 nest启动以后就会从 AppModule
开始解析 class 上通过装饰器声明的依赖信息,自动创建和组装对象,也就是自动帮我们做了new的操作。这样以来,我们就可以在controller
中直接调用service
中的方法了:
同样的,当再来一个别的模块,比如我们创建一个person
模块:
nest g resource person --no-spec
我们在PersonModule
中exports一个PersonService
:
import { Module } from '@nestjs/common';
import { PersonService } from './person.service';
import { PersonController } from './person.controller';
@Module({
controllers: [PersonController],
providers: [PersonService],
exports: [PersonService],
})
export class PersonModule {}
然后在AppModule
importsPersonModule
,
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PersonModule } from './person/person.module';
@Module({
imports: [PersonModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
这样一来我们是不是就可以在AppController
中使用PersonModule
中的PersonService
了,因为它已经被注入了IOC容器了:
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
import { PersonService } from './person/person.service';
@Controller()
export class AppController {
constructor(
private readonly appService: AppService,
private readonly personService: PersonService,
) {}
@Get()
getHello(): string {
console.log(this.personService.findAll());
return this.appService.getHello();
}
}
可以看到打印日志:
已经成功的调用了PersonService
中的findAll
方法了。
这就是模块如何引用模块的方式,我们通过OC依赖注入的这种思想,来维护整个项目模块之间的依赖,我们不需要关系它们是如何注入的,我们只是需要在使用它的地方注入就行了,这就是 Nest 的 IOC 机制。你懂了没有?
最后总结一句:
Nest 里通过 @Controller 声明可以被注入的 controller,通过 @Injectable 声明可以被注入也可以注入别的对象的 provider,然后在 @Module 声明的模块里引入。
转载自:https://juejin.cn/post/7242198986261676093