Nest 应用程序及其各个组成部分,都遵循框架统一的生命周期管理机制。为了方便在不同阶段执行特定逻辑,Nest 提供了一套生命周期钩子(Lifecycle Hooks)。通过这些钩子,你可以在模块、提供者或控制器的初始化与销毁等关键节点,插入自定义的处理逻辑,实现资源初始化、清理等操作。
下图展示了从应用启动到 Node.js 进程退出期间,各个生命周期事件的调用顺序。整个生命周期可以划分为三个阶段:初始化(initializing)、运行中(running) 和终止中(terminating)。
掌握这三个阶段的执行流程,有助于你更合理地规划服务的初始化和资源管理,例如在合适的时机建立连接或注册任务。同时,也能帮助你在接收到关闭信号时,实现应用的优雅退出,确保资源被正确释放,系统平稳停机。
在应用启动和关闭的过程中,Nest 会依照特定的生命周期阶段,自动调用已注册的生命周期钩子方法。这些钩子可定义在模块、提供者或控制器中,用于在特定时机插入自定义逻辑(例如资源初始化或清理)。需要注意的是,部分关闭阶段的钩子需要显式启用,具体可参考后文说明。
如上图所示,Nest 在调用各类钩子的同时,也会在底层负责管理应用的连接监听状态(例如启动或停止 HTTP 服务器)。
需要注意的是,部分生命周期钩子仅在应用显式调用 app.init() 或 app.listen() 时才会被触发,例如:
onModuleInitonApplicationBootstrap而应用关闭阶段的钩子 —— 如 onModuleDestroy、beforeApplicationShutdown 和 onApplicationShutdown,则需在调用 app.close(),或进程接收到诸如 SIGTERM 等系统信号时才会生效。此外,响应系统信号还要求显式启用关闭钩子。
| 生命周期钩子方法 | 触发时机 |
|---|---|
onModuleInit() | 当宿主模块的所有依赖关系解析完成后调用。 |
onApplicationBootstrap() | 所有模块均已初始化完成,但在应用开始监听端口或建立连接之前调用。 |
onModuleDestroy()* | 当应用接收到终止信号(如 SIGTERM)时触发。 |
beforeApplicationShutdown()* | 在所有模块的 onModuleDestroy() 钩子执行完毕(相关 Promise 已解析或拒绝)后调用;随后应用将关闭所有连接,相当于执行 app.close()。 |
onApplicationShutdown()* | 在应用的所有连接关闭完成(即 app.close() 执行并解析后)调用。 |
* 上述钩子不会在默认情况下响应系统信号,需调用 app.enableShutdownHooks() 主动启用。
生命周期钩子不会在请求作用域类中触发。这是因为请求作用域类的实例在每个请求中动态创建,生命周期受请求驱动,且会在响应结束后被自动销毁,不参与应用生命周期管理。
onModuleInit() 和 onApplicationBootstrap()
钩子的执行顺序会严格按照模块的导入顺序依次调用,并且只有当前模块的钩子方法执行完成后,才会继续执行下一个模块的同一钩子。
Nest 为每个生命周期钩子都提供了对应的接口。虽然这些接口在 TypeScript 编译后并不会被保留,因此并不是强制要求实现,但建议这么做:这样不仅可以获得类型检查的保护,还能享受更完善的编辑器智能提示。
要使用某个生命周期钩子,只需在相应的类(如控制器、提供者或模块)中实现对应的接口,并定义相应的方法。例如,如果你想在模块初始化时执行一些自定义逻辑,只需实现 OnModuleInit 接口,并在类中添加 onModuleInit() 方法,如下所示:
import { Injectable, OnModuleInit } from '@nestjs/common'
@Injectable()
export class UsersService implements OnModuleInit {
onModuleInit() {
console.log('该模块初始化完成')
}
}OnModuleInit 和 OnApplicationBootstrap 等生命周期钩子同样支持异步操作。也就是说,这些钩子方法既可以返回 Promise,也可以通过 async/await 来处理异步任务。借助这一特性,我们可以在模块初始化或应用启动阶段执行需要等待完成的逻辑,从而有序地延迟后续流程的继续推进,确保初始化过程满足特定依赖条件。
async onModuleInit(): Promise<void> {
await this.fetch()
}在应用终止时,Nest 会依次触发以下生命周期钩子:
onModuleDestroy()beforeApplicationShutdown()onApplicationShutdown()这些钩子会在两种情况下被执行:
app.close() 时。SIGTERM)且已手动开启了「关闭钩子监听」时。这一机制在容器化部署环境中尤为关键,例如在 Kubernetes 中配合 Pod 生命周期管理,或在 Heroku 等平台上优雅地关闭 dyno 实例。
出于性能考虑,Nest 默认不会启用关闭钩子监听器。如果需要使用,必须显式调用 enableShutdownHooks() 方法:
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
// 启用关闭钩子
app.enableShutdownHooks()
await app.listen(process.env.PORT ?? 3000)
}由于平台机制限制,Nest 在 Windows 上对关闭钩子的支持存在差异。例如 SIGINT、SIGBREAK 和部分 SIGHUP 通常可以正常工作(详见 Node.js 信号文档)。但 SIGTERM 在 Windows 中始终无效,因为任务管理器直接终止进程时不会发送此信号,应用也无法感知或阻止该行为。
推荐阅读: libuv 信号文档
启用关闭钩子会注册进程监听器,增加内存占用。在单个 Node 进程中运行多个 Nest
应用(如使用 Jest 并行测试)时,可能因监听器过多而触发 Node
的警告提示。因此,enableShutdownHooks()
默认关闭,使用时请评估实例数量和资源情况。
当应用收到终止信号(例如 SIGINT),Nest 会依照以下顺序调用生命周期钩子:
onModuleDestroy()beforeApplicationShutdown()onApplicationShutdown(signal: string)如果这些钩子方法是异步的(即返回 Promise),Nest 会等待每个 Promise 被解析或拒绝后再继续执行下一个钩子。
示例:
@Injectable()
class UsersService implements OnApplicationShutdown {
onApplicationShutdown(signal: string) {
console.log(signal) // 输出接收到的信号,例如 "SIGINT"
}
}app.close() 不会关闭 Node.js 进程,只会触发 onModuleDestroy() 和
onApplicationShutdown()
钩子。如果应用中存在定时器、长时间运行的任务等,进程仍会继续运行,需手动清理或调用
process.exit()。