Skip to content

Service Provider

简介

Service provider 是所有 Laravel 应用程序引导的核心位置。你自己的应用程序以及所有 Laravel 的核心服务都是通过 service provider 引导的。

但是,我们所说的"引导"是什么意思呢?一般来说,我们指的是注册事物,包括注册 service container 绑定、事件监听器、middleware,甚至路由。Service provider 是配置你的应用程序的核心位置。

Laravel 内部使用数十个 service provider 来引导其核心服务,如邮件发送器、队列、缓存等。其中许多 provider 是"延迟加载"的 provider,这意味着它们不会在每个请求中加载,而只在实际需要它们提供的服务时才加载。

所有用户定义的 service provider 都在 bootstrap/providers.php 文件中注册。在以下文档中,你将学习如何编写自己的 service provider 并将它们注册到 Laravel 应用程序中。

NOTE

如果你想了解更多关于 Laravel 如何处理请求及其内部工作原理,请查看我们关于 Laravel 请求生命周期 的文档。

编写 Service Provider

所有 service provider 都扩展 Illuminate\Support\ServiceProvider 类。大多数 service provider 包含 registerboot 方法。在 register 方法中,你应该只将事物绑定到 service container。你永远不应尝试在 register 方法中注册任何事件监听器、路由或任何其他功能。

Artisan CLI 可以通过 make:provider 命令生成新的 provider。Laravel 会自动将你的新 provider 注册到应用程序的 bootstrap/providers.php 文件中:

shell
php artisan make:provider RiakServiceProvider

Register 方法

如前所述,在 register 方法中,你应该只将事物绑定到 service container 中。你永远不应尝试在 register 方法中注册任何事件监听器、路由或任何其他功能。否则,你可能会意外使用尚未加载的 service provider 提供的服务。

让我们看一个基本的 service provider。在任何 service provider 方法中,你始终可以访问 $app 属性,该属性提供对 service container 的访问:

php
<?php

namespace App\Providers;

use App\Services\Riak\Connection;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;

class RiakServiceProvider extends ServiceProvider
{
    /**
     * 注册任何应用程序服务。
     */
    public function register(): void
    {
        $this->app->singleton(Connection::class, function (Application $app) {
            return new Connection(config('riak'));
        });
    }
}

这个 service provider 只定义了一个 register 方法,并使用该方法在 service container 中定义 App\Services\Riak\Connection 的实现。如果你还不熟悉 Laravel 的 service container,请查看其文档

bindingssingletons 属性

如果你的 service provider 注册了许多简单的绑定,你可能希望使用 bindingssingletons 属性,而不是手动注册每个容器绑定。当框架加载 service provider 时,它会自动检查这些属性并注册它们的绑定:

php
<?php

namespace App\Providers;

use App\Contracts\DowntimeNotifier;
use App\Contracts\ServerProvider;
use App\Services\DigitalOceanServerProvider;
use App\Services\PingdomDowntimeNotifier;
use App\Services\ServerToolsProvider;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * 应该注册的所有容器绑定。
     *
     * @var array
     */
    public $bindings = [
        ServerProvider::class => DigitalOceanServerProvider::class,
    ];

    /**
     * 应该注册的所有容器单例。
     *
     * @var array
     */
    public $singletons = [
        DowntimeNotifier::class => PingdomDowntimeNotifier::class,
        ServerProvider::class => ServerToolsProvider::class,
    ];
}

Boot 方法

那么,如果我们需要在 service provider 中注册 view composer 怎么办?这应该在 boot 方法中完成。此方法在所有其他 service provider 都已注册后调用,这意味着你可以访问框架已注册的所有其他服务:

php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

class ComposerServiceProvider extends ServiceProvider
{
    /**
     * 引导任何应用程序服务。
     */
    public function boot(): void
    {
        View::composer('view', function () {
            // ...
        });
    }
}

Boot 方法依赖注入

你可以为 service provider 的 boot 方法类型提示依赖项。Service container 会自动注入你需要的任何依赖项:

php
use Illuminate\Contracts\Routing\ResponseFactory;

/**
 * 引导任何应用程序服务。
 */
public function boot(ResponseFactory $response): void
{
    $response->macro('serialized', function (mixed $value) {
        // ...
    });
}

注册 Provider

所有 service provider 都在 bootstrap/providers.php 配置文件中注册。此文件返回一个包含应用程序 service provider 类名的数组:

php
<?php

return [
    App\Providers\AppServiceProvider::class,
];

当你调用 make:provider Artisan 命令时,Laravel 会自动将生成的 provider 添加到 bootstrap/providers.php 文件中。但是,如果你手动创建了 provider 类,则应手动将 provider 类添加到数组中:

php
<?php

return [
    App\Providers\AppServiceProvider::class,
    App\Providers\ComposerServiceProvider::class, // [tl! add]
];

延迟加载 Provider

如果你的 provider 仅仅service container 中注册绑定,你可以选择延迟其注册,直到实际需要其中一个注册的绑定时才加载。延迟加载这样的 provider 将提高应用程序的性能,因为它不会在每个请求中从文件系统加载。

Laravel 会编译并存储由延迟 service provider 提供的所有服务的列表以及其 service provider 类名。然后,只有当你尝试解析这些服务之一时,Laravel 才会加载该 service provider。

要延迟 provider 的加载,请实现 \Illuminate\Contracts\Support\DeferrableProvider 接口并定义 provides 方法。provides 方法应返回 provider 注册的 service container 绑定:

php
<?php

namespace App\Providers;

use App\Services\Riak\Connection;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class RiakServiceProvider extends ServiceProvider implements DeferrableProvider
{
    /**
     * 注册任何应用程序服务。
     */
    public function register(): void
    {
        $this->app->singleton(Connection::class, function (Application $app) {
            return new Connection($app['config']['riak']);
        });
    }

    /**
     * 获取 provider 提供的服务。
     *
     * @return array<int, string>
     */
    public function provides(): array
    {
        return [Connection::class];
    }
}