Yii应用启动时,bootstrap组件在Application基本构造完成后、响应请求前被强制预热并调用init();顺序影响依赖访问,BootstrapInterface更可控且适合跨组件协调。

组件在 bootstrap 阶段初始化的时机和顺序
Yii 应用启动时,bootstrap 配置项里列出的组件会**在应用主体(Application)完成基本构造后、但尚未响应请求前**被提前实例化并调用其 init() 方法。这不是“懒加载”,而是强制预热。
常见错误是以为写进 bootstrap 就等于“自动注册事件”或“全局可用”——其实它只保证组件对象被创建并执行了 init(),后续是否绑定事件、是否挂载到应用属性上,得看组件自己怎么写。
-
bootstrap里的组件名必须已在components中定义过,否则报错:Unknown component ID: xxx - 顺序有影响:靠前的组件在
init()里访问靠后的组件,可能拿到未初始化的对象(因为 Yii 是按数组顺序逐个getComponent()的) - 如果组件
init()中依赖Yii::$app->request等运行时对象,没问题;但依赖Yii::$app->response可能出问题——此时 response 还没完全 setup
BootstrapInterface 和手动触发 bootstrap() 的区别
实现 BootstrapInterface 的类,必须提供 bootstrap($app) 方法,这个方法由 Yii 在 Application::bootstrap() 中统一调用,且传入的是完整的 $app 实例。它比单纯靠 bootstrap 数组触发 init() 更可控,也更适合做跨组件协调。
典型场景:日志组件要等 DB 组件就绪后再配置目标,或者权限组件需要在 request 解析后才注册事件。
- 接口方式不依赖组件是否在
components中注册;它可以是纯服务类,甚至匿名类 -
bootstrap数组里写类名字符串,Yii 会 new 出来并调bootstrap($app);写组件 ID,则先 getComponent 再检查是否实现了接口 - 别在
bootstrap()里做耗时操作(如查库、发 HTTP),会拖慢所有请求的冷启动
事件绑定写在 init() 还是 bootstrap()?
取决于事件监听目标是否存在、是否已 ready。多数情况下,绑在 init() 是安全的,因为 Application 对象已经存在,on() 调用不会失败;但监听的事件源(比如 beforeRequest)还没触发,所以没问题。
容易踩的坑是:在 init() 里绑了 Yii::$app->on(Event::EVENT_BEFORE_REQUEST, ...),结果发现回调没执行——原因往往是组件被声明在 bootstrap 里,但事件本身在更早阶段(如入口脚本中)就被触发了。
- 推荐统一在
bootstrap()(接口方式)中绑定事件,确保它发生在所有核心组件就绪之后、第一个事件分发之前 - 如果必须在
init()绑,记得检查Yii::$app->has('request')或Yii::$app->has('response'),避免空指针 - 重复绑定同一事件不会报错,但会导致多次触发;建议加
off()再on(),或用once()
bootstrap 配置项对控制台命令的影响
控制台应用(yii\console\Application)同样支持 bootstrap,但行为略有不同:它会在命令执行前触发,且每个命令都走一遍(除非缓存了应用实例)。这意味着,如果你的 bootstrap 组件做了单例状态写入(如静态变量、全局缓存),多个命令之间可能互相污染。
典型错误现象:yii migrate 执行完,再跑 yii rbac/assign,发现 RBAC 数据异常——很可能是因为某个 bootstrap 组件在第一次运行时修改了共享状态,第二次没重置。
- 控制台命令默认不启用
bootstrap,需显式配置('bootstrap' => ['log', 'myHelper']) - 避免在 bootstrap 组件中使用静态属性保存上下文;改用
$app->params或依赖注入容器 - 调试时可临时注释
bootstrap数组,确认问题是否由此引入——这是最直接的隔离手段
bootstrap、哪个该延迟加载、哪个根本不需要——这得看它和请求生命周期的耦合深度。


















