bind()每次调用app()->make()都新建实例,适合无状态轻量类;singleton()首次创建后全程复用同一实例,必须在register()中绑定,适用于数据库连接等有状态服务。

bind() 用在需要每次新建实例的地方,singleton() 才是管“整个请求生命周期只一个实例”的正解;把 singleton() 写在 boot() 里、或对有状态服务用 bind(),就会出现连接复用失效、缓存错乱、测试污染等典型作用域错误。
为什么 bind() 每次都新建,但你以为它该复用?
因为 bind() 注册的是工厂逻辑,不是实例本身。每次调用 app()->make(),容器都会重新执行闭包或反射构造,不缓存结果。
- 常见误用:把数据库连接类用
bind()绑定 → 每个控制器、每个 Repository、每个队列任务都 new 一次 PDO 实例 → 连接池撑爆,报PDOException: SQLSTATE[HY000] [1040] Too many connections - 正确场景:DTO、Request 类、策略类(如
PaymentStrategyInterface的不同实现)——无状态、轻量、可随意重建 - 验证方式:
app()->make('db') === app()->make('db')返回false就说明是bind()行为,不是你想要的“单例”
singleton() 必须在 register() 里绑定,boot() 太晚了
Laravel 容器在 register() 阶段完成所有绑定注册,到 boot() 时,核心服务(如 router、cache、db)已被解析过一轮;此时再调 singleton(),绑定会被跳过,后续 make() 仍走默认逻辑或 fallback 到自动反射。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 典型现象:本地开发正常,但队列任务或 Artisan 命令中复现
cache实例不一致、Redis 连接重复创建 - 修复动作:把
$this->app->singleton(CacheContract::class, RedisCache::class);从boot()移到register() - 额外注意:若绑定依赖其他已解析服务(比如要
$app->make('config')),必须确保该服务已在register()早期可用,否则会抛BindingResolutionException
instance() 是硬塞实例,不校验依赖是否 ready
instance() 完全绕过容器的依赖解析机制,直接把传入对象存进键值对。它不关心这个对象的构造参数有没有被注入、环境变量是否加载、配置是否生效。
- 高危操作:在
.env还没读取完时就$this->app->instance(SmsService::class, new TwilioSmsService());→ 后续调用$sms->send()直接崩在Undefined index: TWILIO_SID - 安全写法:确保所有依赖已初始化,例如用
config('services.twilio')提前兜底,或只在boot()中对已知就绪的 SDK 实例做instance() - 不可逆性:同一个抽象先
instance()后singleton(),后者不会覆盖前者 ——make()永远返回你硬塞的那个实例
真正容易被忽略的点是:作用域错误往往不报错,只在高并发、长运行或跨进程场景下才暴露。比如队列 worker 持续运行数小时后,bind() 出来的日志实例开始丢上下文,或 singleton() 缓存的 token 因未刷新而过期 —— 这些都不是语法问题,而是生命周期管理没对齐应用实际运行模型。

















