> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 服务容器

> 介绍 Laravel 服务容器的依赖注入机制以及绑定的基础用法。

## 什么是服务容器

Laravel 的服务容器是用来管理类依赖并进行依赖注入的机制。所谓依赖注入，指的是通过构造器或（在某些情况下）setter 方法，将类所需的依赖「注入」到类中。

来看下面的示例：

```php theme={null}
<?php

namespace App\Http\Controllers;

use App\Services\AppleMusic;
use Illuminate\View\View;

class PodcastController extends Controller
{
    /**
     * 创建新的控制器实例
     */
    public function __construct(
        protected AppleMusic $apple,
    ) {}

    /**
     * 显示指定 podcast 的信息
     */
    public function show(string $id): View
    {
        return view('podcasts.show', [
            'podcast' => $this->apple->findPodcast($id)
        ]);
    }
}
```

此示例中，`PodcastController` 需要从 Apple Music 等数据源获取 Podcast。因此我们将能够获取 Podcast 的服务**注入**给它。这样一来，在测试时就可以很方便地替换 `AppleMusic` 服务的 mock（假实现）。

<Info>
  深入理解服务容器对于构建大型 Laravel 应用不可或缺，也有助于为 Laravel 核心贡献代码。
</Info>

```mermaid theme={null}
flowchart TD
    A["服务提供者<br>register()"] --> B["服务容器<br>注册绑定"]
    B --> C{"解析请求<br>make() / 自动注入"}
    C -- "具体类" --> D["通过反射<br>自动解析"]
    C -- "接口" --> E["解析出<br>已注册的实现类"]
    D --> F["生成实例<br>注入到构造器"]
    E --> F
```

## 零配置解析

如果类只依赖其他具体类（而不是接口），就无需告诉容器如何解析它。例如你在 `routes/web.php` 中写下这段代码：

```php theme={null}
<?php

class Service
{
    // ...
}

Route::get('/', function (Service $service) {
    dd($service::class);
});
```

<Info>
  这个例子把类定义在路由文件内只是为了演示。真实应用中，服务类请放到 `app/Services` 目录下。
</Info>

访问该路由时，Laravel 会自动解析 `Service` 类并注入到路由处理器中。你无需任何配置就能享受依赖注入。

Laravel 应用中编写的许多类（控制器、事件监听器、中间件等）都会通过容器自动注入依赖。

## 绑定

### 基本绑定

大部分绑定都会在[服务提供者](/zh/service-providers)中注册。在服务提供者中可以通过 `$this->app` 属性访问容器。

#### bind

`bind` 方法把类或接口名与一个闭包组合，注册为绑定。

```php theme={null}
use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;

$this->app->bind(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});
```

闭包会接收容器本身作为参数，可以用它解析子依赖。

如果想在服务提供者之外操作容器，请使用 `App` 门面。

```php theme={null}
use App\Services\Transistor;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\App;

App::bind(Transistor::class, function (Application $app) {
    // ...
});
```

<Info>
  不依赖接口的类无需向容器注册绑定。容器可以通过反射自动解析这类对象。
</Info>

#### singleton

`singleton` 方法会让某个类或接口只解析一次。解析出来的单例，后续对容器的调用都会返回同一实例。

```php theme={null}
use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;

$this->app->singleton(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});
```

#### instance

也可以通过 `instance` 方法把已有的对象实例绑定到容器上。之后对该绑定的解析都会返回该实例。

```php theme={null}
use App\Services\Transistor;
use App\Services\PodcastParser;

$service = new Transistor(new PodcastParser);

$this->app->instance(Transistor::class, $service);
```

### 将接口绑定到实现

服务容器的一个强大功能是可以把接口绑定到特定实现。以 `EventPusher` 接口与 `RedisEventPusher` 实现为例：

```php theme={null}
use App\Contracts\EventPusher;
use App\Services\RedisEventPusher;

$this->app->bind(EventPusher::class, RedisEventPusher::class);
```

之后，容器就会给需要 `EventPusher` 实现的类注入 `RedisEventPusher`。你只需在构造器中类型提示 `EventPusher` 接口即可。

```php theme={null}
use App\Contracts\EventPusher;

/**
 * 创建新的类实例
 */
public function __construct(
    protected EventPusher $pusher,
) {}
```

<Tip>
  依赖接口后，即使替换实现也无需修改代码，测试和未来的更改都更方便。
</Tip>

## 自动解析（通过类型提示进行 DI）

服务容器在解析控制器、事件监听器、中间件等类时，会读取构造器的类型提示并自动注入依赖。

```php theme={null}
<?php

namespace App\Http\Controllers;

use App\Repositories\UserRepository;

class UserController extends Controller
{
    /**
     * 创建新的控制器实例
     */
    public function __construct(
        protected UserRepository $users,
    ) {}
}
```

只要 `UserRepository` 不依赖接口，就无需向容器注册。当访问对应路由时，容器会自动解析依赖并注入控制器。

## 从容器解析

### make 方法

使用 `make` 方法可以从容器中解析类实例。

```php theme={null}
use App\Services\Transistor;

$transistor = app()->make(Transistor::class);
```

若某个依赖无法被容器解析，可以使用 `makeWith` 方法额外传入参数。

```php theme={null}
$transistor = $this->app->makeWith(Transistor::class, ['id' => 1]);
```

### 自动注入

实际开发中很少直接调用 `make`。只要在容器负责解析的类（控制器、事件监听器、中间件等）的构造器上加上类型提示，容器就会自动注入依赖。

## 门面与容器的关系

Laravel 的门面为容器中的对象提供了静态接口。例如，`Cache::get()` 在内部会从容器获取 `Cache` 服务并调用。

```php theme={null}
use Illuminate\Support\Facades\Cache;

// 通过门面调用
Cache::get('key');

// 直接使用容器的等价调用
app('cache')->get('key');
```

门面是容器的便捷包装。测试时也可以把门面替换成 mock。

```php theme={null}
use Illuminate\Support\Facades\Cache;

Cache::shouldReceive('get')
    ->once()
    ->with('key')
    ->andReturn('value');
```

## 构造器注入实战示例

来看实际应用中一种典型模式。

<Steps>
  <Step title="定义接口">
    ```php theme={null}
    <?php

    namespace App\Contracts;

    interface PaymentGateway
    {
        public function charge(int $amount, string $token): bool;
    }
    ```
  </Step>

  <Step title="创建实现类">
    ```php theme={null}
    <?php

    namespace App\Services;

    use App\Contracts\PaymentGateway;

    class StripePaymentGateway implements PaymentGateway
    {
        public function charge(int $amount, string $token): bool
        {
            // 使用 Stripe API 进行扣款...
            return true;
        }
    }
    ```
  </Step>

  <Step title="在服务提供者中绑定">
    ```php theme={null}
    use App\Contracts\PaymentGateway;
    use App\Services\StripePaymentGateway;

    $this->app->singleton(PaymentGateway::class, StripePaymentGateway::class);
    ```
  </Step>

  <Step title="在控制器中接收注入">
    ```php theme={null}
    <?php

    namespace App\Http\Controllers;

    use App\Contracts\PaymentGateway;
    use Illuminate\Http\Request;

    class OrderController extends Controller
    {
        public function __construct(
            protected PaymentGateway $payment,
        ) {}

        public function store(Request $request)
        {
            $this->payment->charge(
                $request->amount,
                $request->payment_token
            );

            // ...
        }
    }
    ```
  </Step>
</Steps>

采用这种模式后，即使把支付服务从 `Stripe` 换成其他供应商，也只需修改一处绑定。

## 下一步

<Card title="服务提供者" icon="plug" href="/zh/service-providers">
  学习如何通过服务提供者注册绑定。
</Card>


## Related topics

- [服务提供者](/zh/service-providers.md)
- [请求生命周期](/zh/lifecycle.md)
- [实现自定义认证 Guard](/zh/advanced/custom-auth-guard.md)
- [Laravel 11 之后的应用结构](/zh/advanced/app-structure.md)
- [用 Laravel 构建 MCP 服务器](/zh/advanced/mcp-server.md)
