bind每次解析新建实例,singleton首次解析后复用同一实例;前者适合无状态轻量类,后者适用于有状态或资源密集型服务。

Laravel 6 的服务容器中,bind 和 singleton 的核心区别在于:每次解析是否新建实例。这不是语法差异,而是直接影响对象生命周期、内存占用和状态共享的关键设计选择。
bind:每次 make 都创建全新实例
调用 app()->make() 或通过构造函数自动注入时,容器会执行绑定的闭包或反射类,生成一个独立对象。该对象不被缓存,下次调用仍会重新构造。
- 适合无状态、轻量级类,比如 DTO、表单请求类、策略类、字符串工具类
- 避免多个使用者意外共享内部属性(如临时缓存、计数器)
- 错误示例:用
bind绑定数据库连接 → 每次请求都新建连接,快速耗尽连接池 - 正确写法:
$this->app->bind(FormatterInterface::class, JsonFormatter::class);
singleton:首次 make 后永久复用同一实例
容器在第一次解析时执行构造逻辑,并将实例存入内部 $instances 数组;后续所有 make() 调用直接返回该缓存对象,不再重复初始化。
- 适用于有状态、资源密集型服务,如日志处理器、缓存仓库、HTTP 客户端、数据库连接器
- 验证方式:
app('cache') === app('cache')必须为true - 常见陷阱:在
boot()中调用singleton()→ 此时容器已开始解析依赖,绑定可能被跳过 - 接口绑定也支持单例:
$this->app->singleton(CacheContract::class, RedisCache::class);
底层机制与使用要点
singleton() 实际是 bind($abstract, $concrete, true) 的封装,第三个参数 $shared = true 触发缓存行为。而 bind() 默认 $shared = false,不缓存结果。
- 绑定必须放在服务提供者的
register()方法中,确保在依赖解析前完成 - 闭包绑定时,务必通过
$app参数调用$app->make()解析依赖,不可手动 new 并忽略依赖注入 - 单元测试中若修改了 singleton 实例的状态,需主动调用
app()->forgetInstance(YourClass::class)清理,防止测试污染 - 不要用
instance()替代singleton(),前者注入的是已初始化对象,无法享受容器的依赖自动解析能力
怎么选?看两个关键问题
这个服务是否需要全局唯一?它的初始化是否开销大或带外部资源?
- 是 → 用
singleton() - 否 → 用
bind() - 不确定 → 先用
bind(),观察状态是否错乱或性能是否异常,再调整


















