全局模块和生命周期
全局模块
如果一个模块被很多个模块依赖了,那么每次 import 就会很麻烦。
最好的方法是使用@Global装饰器设置成全局模块。
ts1// 当前 Module 属于 AppModule 模块 2@Global() 3@Module({ 4 controllers: [AppController, UserController], 5 providers: [AppService], 6}) 7export class AppModule {}
这样在其他模块下可以直接注入,不需要再 import。
ts1// 当前 Controller 属于 User 模块 2@Controller('/api/user') 3export class UserController { 4 constructor( 5 @Inject(AppService) 6 private readonly appService: AppService, 7 ) {} 8 9 @Get() 10 getService() { 11 return this.appService.getHello() 12 } 13}
生命周期方法
Nest 在启动的时候,会递归解析 Module 依赖,扫描其中的 provider、controller,注入它的依赖。
全部解析完后,会监听网络端口,开始处理请求。
在这个过程中,暴露了一些 API,根据顺序我们称之为生命周期方法:

当应用启动时:
- 首先,递归初始化模块,依次调用模块内的 Controller、provider 的
onModuleInit,再调用模块的onModuleInit方法。 - 全部初始化完之后,再依次调用模块内的 controller、provider 的 onApplicationBootstrap 方法,然后调用 module 的 onApplicationBootstrap 方法
- 然后是监听端口。
- 最后 Nest 应用运行
我们来测试一下:
bash1nest generate resource user
然后在 UserModule 实现onModuleInit和onApplicationBootstrap方法:
ts1export class UserModule implements OnApplicationBootstrap, OnModuleInit { 2 onModuleInit() { 3 console.log('user module init') 4 } 5 onApplicationBootstrap() { 6 console.log('user module bootstrap') 7 } 8}
并在 UserController 实现这两个方法:
ts1@Controller('user') 2export class UserController implements OnApplicationBootstrap, OnModuleInit { 3 constructor(private readonly userService: UserService) {} 4 onApplicationBootstrap() { 5 console.log('user controller bootstrap') 6 } 7 onModuleInit() { 8 console.log('user controller init') 9 } 10}
最后在 AppModule 处实现这两个方法:
ts1export class AppModule implements OnApplicationBootstrap, OnModuleInit { 2 onModuleInit() { 3 console.log('app module init') 4 } 5 onApplicationBootstrap() { 6 console.log('app module bootstrap') 7 } 8}
最后启动应用,打开终端,会看到以下日志:
bash1user controller init 2user module init 3app module init 4user controller bootstrap 5user module bootstrap 6app module bootstrap
可以看到如官方所述,从UserModule到AppModule,从controller 到 module, Nest 确实是自下而上按照顺序递归执行这两个生命周期方法。
当应用销毁时:
- 先调用每个模块的 controller、provider 的 onModuleDestroy 方法,然后调用 Module 的 onModuleDestroy 方法。
- 之后再调用每个模块的 controller、provider 的 beforeApplicationShutdown 方法,然后调用 Module 的 beforeApplicationShutdown 方法。
- 然后停止监听网络端口。
- 之后调用每个模块的 controller、provider 的 onApplicationShutdown 方法,然后调用 Module 的 onApplicationShutdown 方法。
- 停止进程
beforeApplicationShutdown和onModuleDestroy有一个区别,我们来查看 nest 对他们的接口声明:
ts1export interface OnModuleDestroy { 2 onModuleDestroy(): any 3}
beforeApplicationShutdown接受一个 signal。
ts1export interface BeforeApplicationShutdown { 2 beforeApplicationShutdown(signal?: string): any 3}
这说明 beforeApplicationShutdown 方法可以接受 signal 系统信号,比如 SIGTERM。
这些终止信号是别的进程传过来的,让它做一些销毁的事情,比如用 k8s 管理容器的时候,可以通过这个信号来通知它。
现在我们在 AppModule 里实现一下他们:
ts1@Module({ 2 controllers: [AppController], 3 providers: [AppService], 4 imports: [UserModule], 5}) 6export class AppModule implements BeforeApplicationShutdown, OnApplicationShutdown, OnModuleDestroy { 7 beforeApplicationShutdown(signal?: string) { 8 console.log('——————🚀🚀🚀🚀🚀 —— beforeApplicationShutdown:', signal) 9 } 10 onApplicationShutdown(signal?: string) { 11 console.log('——————🚀🚀🚀🚀🚀 —— onApplicationShutdown:', signal) 12 } 13 onModuleDestroy() { 14 console.log('app module destroy') 15 } 16}
接着在main.ts的 bootstrap方法调用 app.close 触发销毁:
ts1async function bootstrap() { 2 const app = await NestFactory.create(AppModule) 3 await app.listen(3000) 4 setTimeout(() => { 5 app.close() 6 }, 3000) 7} 8bootstrap()
三秒钟后,再看 log 信息:
bash1app module destroy 2——————🚀🚀🚀🚀🚀 —— beforeApplicationShutdown: undefined 3——————🚀🚀🚀🚀🚀 —— onApplicationShutdown: undefined
moduleRef
如果想要生命周期函数内,拿到模块对象里的内容(例如 provider),可以使用 moduleRef。
这里的 moduleRef 就是模块的引用对象。

在 onApplicationShutdown 生命周期里,拿到 ModuleRef (模块的引用),调用 get 方法传入 token,获取对应的实例,然后调用它的方法拿到结果。
Log 信息:
bash1——————🚀🚀🚀🚀🚀 —— onApplicationShutdown: Hello World!
总结
如果有一个模块被多个模块所引用,一个比较好的方法是用 @Global 将其变成全局模块。
provider、controller、module 都支持启动和销毁的生命周期函数,这些生命周期函数都支持 async 的方式。
可以在其中做一些初始化、销毁的逻辑,比如 onApplicationShutwon 里通过 moduleRef.get 取出一些 provider,执行关闭连接等销毁逻辑。