NestJS 生命周期钩子
大家好,今天我们来聊一聊 NestJS 框架中一个非常实用但可能被忽视的概念——生命周期钩子。如果你曾经在应用启动时需要做一些初始化工作(比如连接数据库),或者在应用关闭时需要释放资源(比如关闭数据库连接),那么生命周期钩子就是你的救星。它让我们能够在应用的不同阶段插入自定义逻辑,就像是给应用装上了“生命探测器”,随时感知它的状态变化。
什么是生命周期钩子?
简单来说,生命周期钩子就是一些特殊的接口方法,它们会在 NestJS 应用启动和关闭的特定时刻被自动调用。你可以把这些钩子理解为应用的“出生证明”和“遗愿清单”——在应用诞生时执行一些操作,在应用即将消亡时做一些善后工作。
NestJS 为应用提供了两大类生命周期钩子:应用创建时的钩子和应用销毁时的钩子。下面我们分别来看。
一、应用创建时的钩子
当一个 NestJS 应用启动时,它会经历一个复杂的初始化过程:递归解析模块依赖、实例化各个提供者(provider)、控制器(controller)等。在这个过程中,有两个关键的生命周期钩子会被触发:
1. onModuleInit
这个钩子在模块初始化阶段被调用。具体来说,当 Nest 递归初始化每个模块时,它会先执行该模块内所有控制器和提供者的 onModuleInit 方法,然后再执行模块本身的 onModuleInit 方法。
你可以把它想象成一个“班级点名”的过程:老师(模块)先让每个学生(控制器/提供者)举手(执行自己的 onModuleInit),然后老师再总结(执行模块的 onModuleInit)。
使用场景:比如你需要从配置文件中加载一些数据,或者初始化缓存,都可以放在这里做。
2. onApplicationBootstrap
这个钩子在所有模块都初始化完成之后,但在应用开始监听网络端口之前被调用。执行顺序依然是:先执行每个模块内控制器和提供者的 onApplicationBootstrap,再执行模块本身的。
此时,应用的所有依赖都已经准备就绪,你可以安全地执行一些需要全局资源的操作,比如启动后台任务、预热数据等。
触发时机:应用初始化完成 → onApplicationBootstrap → 监听端口 → 正常运行。
二、应用销毁时的钩子
当你的应用需要停止时(比如收到系统关闭信号、手动调用 app.close()),NestJS 也提供了一系列钩子让你优雅地释放资源。销毁过程同样有清晰的顺序。
1. onModuleDestroy
当应用开始销毁流程时,首先触发的是 onModuleDestroy。顺序依然是:先调用每个模块内控制器和提供者的 onModuleDestroy,再调用模块本身的。
这个阶段通常用来做一些轻量级的清理工作,比如清除临时变量、停止某些内部任务。
2. beforeApplicationShutdown
接下来是 beforeApplicationShutdown。它会在 onModuleDestroy 之后、停止网络端口之前被调用。执行顺序同上:先控制器/提供者,后模块。
这个钩子有一个可选参数 signal?: string,可以用来接收外部传入的关闭信号(比如来自 Kubernetes 的 SIGTERM)。你可以根据不同的信号执行不同的销毁逻辑。
3. 停止监听网络端口
在 beforeApplicationShutdown 执行完毕后,Nest 会停止接收新的网络请求(即关闭 HTTP 服务器等)。
4. onApplicationShutdown
最后一步是 onApplicationShutdown。此时网络端口已经关闭,应用不再接受新请求,你可以在这里进行最终的资源释放,比如关闭数据库连接、断开消息队列等。
同样,它也有一个 signal 参数,并且你可以通过 moduleRef.get() 方法获取到需要的提供者实例,然后执行关闭逻辑。
注意:onApplicationShutdown 是在所有模块的控制器/提供者执行完后,最后执行模块本身的。
三、执行顺序总结
为了方便记忆,我们用一张图来概括整个生命周期:
应用启动:
└─ 递归初始化模块
├─ 控制器/提供者的 onModuleInit
└─ 模块的 onModuleInit
└─ 所有模块初始化完成
├─ 控制器/提供者的 onApplicationBootstrap
└─ 模块的 onApplicationBootstrap
└─ 监听网络端口
└─ 应用正常运行
应用关闭:
└─ 收到关闭信号
├─ 控制器/提供者的 onModuleDestroy
└─ 模块的 onModuleDestroy
├─ 控制器/提供者的 beforeApplicationShutdown
└─ 模块的 beforeApplicationShutdown
└─ 停止监听网络端口
├─ 控制器/提供者的 onApplicationShutdown
└─ 模块的 onApplicationShutdown
└─ 进程退出注意:每一个生命周期钩子都支持异步操作,你可以返回一个 Promise 或使用 async/await,Nest 会等待它们完成后再继续下一步。这确保了你在初始化或销毁过程中不会因为异步操作而中断。
四、实际应用场景举例
场景1:启动时连接数据库
假设你使用 TypeORM,并且希望在应用启动时就建立数据库连接,而不是等到第一个请求时才连接。你可以在某个 service 或 module 中实现 onModuleInit:
@Injectable()
export class DatabaseService implements OnModuleInit {
async onModuleInit() {
await this.connectToDatabase();
console.log('数据库连接成功');
}
}场景2:优雅关闭数据库连接
当应用关闭时,你需要确保所有数据库连接都被正确释放,避免连接泄漏:
@Injectable()
export class DatabaseService implements OnApplicationShutdown {
async onApplicationShutdown(signal?: string) {
await this.closeDatabaseConnection();
console.log(`收到信号 ${signal},数据库连接已关闭`);
}
}场景3:Kubernetes 环境下的平滑下线
在 K8s 中,Pod 停止时会发送 SIGTERM 信号。你可以利用 beforeApplicationShutdown 或 onApplicationShutdown 的 signal 参数来区分不同信号,执行不同的逻辑。
五、注意事项
- 生命周期钩子需要在你的类中实现对应的接口(如
OnModuleInit、OnApplicationBootstrap等),并确保该类被 Nest 的 IoC 容器管理(即添加了@Injectable()装饰器)。 - 如果多个提供者都实现了同一个钩子,它们的执行顺序取决于模块中
providers数组的声明顺序。 - 不要在这些钩子中执行耗时过长的操作,否则会阻塞应用启动或关闭。如果确实需要耗时操作,考虑使用异步方式并合理设置超时。

Comments | NOTHING