应用生命周期
本指南介绍 AdonisJS 中的应用生命周期。你将学习:
- 三个生命周期阶段(boot、start、termination)
- 每个阶段何时执行以及执行期间发生了什么
- 如何使用服务提供者(service provider)和预加载文件(preload file)钩入这些阶段
概述
AdonisJS 中的应用生命周期由三个不同的阶段组成:boot(启动)、start(运行)和 termination(终止)。每个阶段在为你的应用做准备、运行它以及优雅地关闭它的过程中,都扮演着一个特定的角色。
当你需要在应用运行时的特定时刻执行代码时,理解生命周期至关重要。例如,你可能想在应用开始处理请求之前注册自定义的验证规则,或者在应用终止之前执行清理操作。
生命周期按时间顺序从 boot 流向 start,最终在进程收到关闭信号时进入 termination。每个阶段都有明确定义的职责,并按可预测的顺序发生,使你能够钩入你需要的精确时刻。
Boot 阶段
boot 阶段是 AdonisJS 为执行准备应用的初始阶段。在此阶段,你可以使用 IoC 容器来获取绑定并扩展框架的各个部分。
服务提供者将它们的绑定注册到容器中,并执行它们的 boot 方法。框架本身正在被配置,但你的应用尚未准备好处理请求或执行命令。
boot 阶段会在任何预加载文件被导入或应用特定的代码运行之前完成。将它视为框架组装其所需所有部件的基础奠基阶段。
Start 阶段
start 阶段是你的应用「活起来」的阶段。在此阶段,AdonisJS 会导入预加载文件,并执行来自服务提供者的 start 和 ready 方法。
应用特定的初始化发生在这里。路由被注册、事件监听器被附加,设置代码被运行。在此阶段结束时,你的应用已完全可运行,并准备好根据你的环境来处理 HTTP 请求、执行 Ace 命令或运行测试。
start 阶段是环境感知的,这意味着你可以为 HTTP 服务器、Ace 命令或测试环境配置不同的行为。为当前环境配置的所有预加载文件会被并行导入,以获得最佳性能。
Termination 阶段
termination 阶段发生在 AdonisJS 开始优雅关闭时。这通常发生在进程收到 SIGTERM 信号时,例如当你停止开发服务器或在进行部署期间。
在此阶段,服务提供者执行它们的 shutdown 方法,使它们能够执行清理操作,例如关闭数据库连接、刷新日志或取消待处理的后台任务。
优雅关闭可确保你的应用干净地停止,而不是在操作过程中突然终止,从而帮助防止数据损坏。
钩入生命周期阶段
你可以使用服务提供者和预加载文件来钩入应用生命周期的不同阶段。服务提供者提供了在特定时刻执行的声明周期方法(boot、start、ready 和 shutdown),而预加载文件则在 start 阶段运行。
钩入 boot 阶段
在服务提供者中使用 boot 方法,以在 boot 阶段执行代码。这是你应该扩展框架或配置应用其他部分所依赖的服务的地方。
以下示例通过自定义 phoneNumber 验证规则扩展了 VineJS。该规则将在你的整个应用中可用。
import { VineString } from '@vinejs/vine'
import type { ApplicationService } from '@adonisjs/core/types'
export default class AppProvider {
constructor(protected app: ApplicationService) {}
async boot() {
VineString.macro('phoneNumber', function (this: VineString) {
return this.use((value, field) => {
if (typeof value !== 'string') {
return
}
if (!/^\d{10}$/.test(value)) {
field.report('The {{ field }} must be a valid 10-digit phone number', field)
}
})
})
}
}
钩入 start 阶段
你可以使用服务提供者方法或预加载文件来钩入 start 阶段。服务提供者提供了 start 和 ready 方法,而预加载文件则为应用特定的初始化提供了一种更简单的方法。
使用服务提供者方法
start 方法在 boot 阶段完成后执行,但在应用就绪之前。ready 方法在应用完全启动并准备好处理请求或命令时执行。
import type { ApplicationService } from '@adonisjs/core/types'
export default class AppProvider {
constructor(protected app: ApplicationService) {}
async start() {
const database = await this.app.container.make('lucid.db')
/**
* 验证数据库连接是否正常工作
*/
await database.connection().select(1)
}
async ready() {
if (this.app.getEnvironment() === 'web') {
const logger = await this.app.container.make('logger')
logger.info('HTTP server is ready to accept requests')
}
}
}
使用预加载文件
预加载文件提供了一种更简单的方式来在 start 阶段运行代码,而无需创建服务提供者。它们非常适合应用特定的初始化,例如注册路由、附加事件监听器或配置中间件。
使用 make:preload 命令创建一个预加载文件。
node ace make:preload events
此命令会在 start 目录中创建一个新文件,并自动在 adonisrc.ts 配置文件中注册它。
import emitter from '@adonisjs/core/services/emitter'
import logger from '@adonisjs/core/services/logger'
emitter.on('user:registered', function (user) {
logger.info({ userId: user.id }, 'New user registered')
})
emitter.on('order:placed', function (order) {
logger.info({ orderId: order.id }, 'New order placed')
})
你可以配置预加载文件,使其仅在特定的运行时环境中加载。
{
preloads: [
() => import('#start/routes'),
() => import('#start/kernel'),
{
file: () => import('#start/events'),
environment: ['web', 'console']
}
]
}
environment 属性接受一个值数组:web(HTTP 服务器)、console(Ace 命令)、test(测试运行器)和 repl(REPL 环境)。
钩入 termination 阶段
在服务提供者中使用 shutdown 方法,以在优雅关闭期间执行清理操作。这可确保在你的应用终止之前正确地释放资源。
import type { ApplicationService } from '@adonisjs/core/types'
export default class AppProvider {
constructor(protected app: ApplicationService) {}
async shutdown() {
const redis = await this.app.container.make('redis')
/**
* 关闭所有 Redis 连接
*/
await redis.quit()
const logger = await this.app.container.make('logger')
logger.info('Application shutdown complete')
}
}
另请参阅
-
服务提供者指南
,了解如何创建和注册服务提供者,并理解
register、boot、start、ready和shutdown方法的完整生命周期 - AdonisRC 文件参考 ,了解配置预加载文件和其他应用设置的完整参考