instance直接注入已创建对象,不解析依赖;singleton由容器首次make时构造并自动注入依赖,后续复用同一实例。

instance 是直接塞入一个已创建好的对象,不走构造逻辑、不解析依赖;singleton 是让容器在首次 make 时执行构造(含依赖注入),之后复用该实例。
instance:跳过容器生命周期,只做“硬注入”
调用 $app->instance(YourClass::class, $obj) 时,容器不做任何初始化动作:
- 传入的对象必须已手动 new 好,且所有依赖需你自行解决
- 容器不会调用构造函数,也不会递归解析类型提示的依赖
- 适合注入外部已存在的实例,比如测试中 mock 的对象、全局配置对象、或第三方 SDK 初始化后的 client
- 错误示例:
$app->instance(Logger::class, new Logger())—— 若 Logger 构造需要 MonologFactory,这里就缺失依赖,运行时可能报错
singleton:由容器控制构造,带完整依赖注入能力
调用 $app->singleton(YourClass::class) 或 $app->singleton(Interface::class, Concrete::class) 时:
- 第一次
app()->make()才触发构造,容器自动解析构造函数参数 - 后续所有
make()都返回同一个实例,状态共享、资源复用 - 支持接口绑定,也支持闭包工厂,例如:
$app->singleton(Cache::class, function ($app) { return new RedisCache($app->make('redis.connection')); }) - 必须放在服务提供者的
register()方法中,否则在boot()里注册可能被忽略
怎么选 instance 还是 singleton?
看这个对象是否需要容器参与构建:
- 你需要它有依赖注入、要走构造函数、要复用但又不想每次新建 → 用 singleton
- 你已经有一个现成对象(比如测试 mock、手动 new 的轻量工具类、或框架外初始化的 SDK 实例)→ 用 instance
- 别用 instance 替代 singleton 来“省事”,否则会丢失自动注入能力,后期扩展困难
常见陷阱提醒
两者都影响测试稳定性:
- 用 singleton 注册的服务,若在测试中修改了其内部状态,必须在 tearDown 中调用
app()->forgetInstance(YourClass::class)清理,否则污染下一个测试 - instance 注入的对象,容器完全不管理其生命周期,也无法通过
forgetInstance清理(它只是存了个引用),清理需你手动处理 - 不要在
boot()中注册 singleton,尤其在队列任务或 Artisan 命令中容易失效


















