Skip to main content

什么是服务容器

Laravel 的服务容器是用于管理类的依赖关系并进行依赖注入的机制。所谓依赖注入,是指将类所需的依赖通过构造函数(或在某些情况下的 setter 方法)“注入”到类中。 来看下面的例子。
在此示例中,PodcastController 需要从 Apple Music 等数据源获取 Podcast。因此我们注入了一个可以获取 Podcast 的服务。通过注入服务,在测试时可以轻松将 AppleMusic 服务替换为 mock(伪实现)。
深入理解服务容器对构建大型 Laravel 应用不可或缺,也有助于对 Laravel 核心本身进行贡献。

零配置解析

如果类只依赖于其他具体类(而不是接口),则无需告诉容器如何解析。例如,将以下代码写入 routes/web.php
该示例在路由文件中定义了类,这仅用于演示。在实际应用中,请将服务类定义在 app/Services 目录下。
访问该路由时,Laravel 会自动解析 Service 类并注入到路由处理器。不必准备配置文件即可享受依赖注入的好处。 在 Laravel 应用中编写的许多类(控制器、事件监听器、中间件等)都会通过容器自动注入依赖。

绑定

基本绑定

大多数绑定都在服务提供者中注册。在服务提供者内可通过 $this->app 属性访问容器。

bind

通过 bind 方法,传入类或接口名与闭包来注册绑定。
闭包的参数中可以接收容器自身。可用它来解析子依赖。 若要在服务提供者之外操作容器,使用 App 门面。
对于不依赖接口的类,无需绑定到容器。容器可以通过反射自动解析这些对象。

singleton

singleton 方法将类或接口绑定为只解析一次。一旦解析后的单例,之后对容器的调用都会返回同一实例。
使用 singletonIf 方法,可以仅在指定类型尚未注册绑定时才注册单例绑定。

Singleton 属性

在类或接口上添加 #[Singleton] 属性,也可以指示容器只解析一次。

绑定作用域单例

scoped 方法将类或接口绑定为在 Laravel 的请求/任务生命周期内只解析一次。它类似于 singleton,但通过 scoped 注册的实例会在 Laravel 应用开始新的”生命周期”时销毁,例如 Laravel Octane Worker 处理新请求时或队列 Worker 处理新任务时。
使用 scopedIf 方法,可以仅在指定类型尚未注册绑定时才注册作用域绑定。

Scoped 属性

在类或接口上添加 #[Scoped] 属性,也可以指示容器在请求/任务生命周期内只解析一次。

instance

也可以通过 instance 方法将现有的对象实例绑定到容器。之后对容器的调用总是返回该实例。

将接口绑定到实现

服务容器的一个强大功能是可以将接口绑定到特定的实现。假设你有 EventPusher 接口和 RedisEventPusher 实现。
这样,容器就会将 RedisEventPusher 注入到需要 EventPusher 实现的类中。之后只需在构造函数中对 EventPusher 接口进行类型提示即可。
依赖接口后,即便替换实现也无需修改代码,从而使测试与未来的变更更加容易。

Bind 属性

Laravel 还提供了更便捷的 Bind 属性。在接口上添加该属性,即可告诉 Laravel 当请求该接口时应自动注入哪个实现。使用 Bind 属性后,无需在服务提供者中进行额外的注册处理。 进一步地,还可以在接口上放置多个 Bind 属性,配置为在不同环境下注入不同的实现。
对于依赖任意条件的绑定,可以使用 BindWhen 属性。可以向闭包传入容器,绑定应用时返回 trueBindBindWhen 属性按声明顺序被求值。
使用 BindWhen 属性需要 PHP 8.5 及以上。
此外,还可以配合 Singleton 属性或 Scoped 属性,指定该容器绑定是解析一次,还是每个请求/任务解析一次。

自动解析(基于类型提示的 DI)

在解析控制器、事件监听器、中间件等类时,服务容器会查看构造函数的类型提示并自动注入依赖。
只要 UserRepository 不依赖于接口,就无需注册到容器。仅需访问该路由,容器就会自动解析依赖并注入到控制器。

从容器解析

make 方法

使用 make 方法可以从容器解析类实例。
若类的依赖无法通过容器解析,也可以使用 makeWith 方法传入额外参数。

自动注入

实际上,你几乎不会直接调用 make 方法。只需在容器所解析的类(控制器、事件监听器、中间件等)的构造函数上添加类型提示,容器就会自动注入。

门面与容器的关系

Laravel 的门面为容器中的对象提供了静态接口。例如,Cache::get() 在内部从容器获取 Cache 服务并调用。
门面是容器的便捷包装。在测试时也可以将门面替换为 mock。

构造函数注入的实践示例

来看在实际应用中的典型模式。
1

定义接口

2

创建实现类

3

在服务提供者中绑定

4

在控制器中接收注入

采用该模式后,即使将支付服务从 Stripe 切换到其他提供商,也只需在一个地方修改绑定。

下一步

服务提供者

学习如何使用服务提供者注册绑定。
最后修改于 2026年8月2日