服務提供者是所有 Laravel 應用程序的引導中心。你的應用程序,以及通過服務器引導的 Laravel 核心服務都是通過服務提供器引導。
但是,「引導」是什麽意思呢?通常,我們可以理解為注冊,比如注冊服務容器綁定,事件監聽器,中間件,甚至是路由。服務提供者是配置應用程序的中心。
當你打開 Laravel 的config/app.php
文件時,你會看到 providers
數組。數組中的內容是應用程序要加載的所有服務提供者的類。當然,其中有很多「延遲」提供者,他們並不會在每次請求的時候都加載,只有他們的服務實際被需要時才會加載。
本篇你將會學到如何編寫自己的服務提供者,並將其注冊到你的 Laravel 應用程序中。
**技巧 ** 如果你想了解有關 Laravel 如何處理請求並在內部工作的更多信息,請查看有關 Laravel 的文檔 請求生命周期。
所有的服務提供者都會繼承Illuminate\Support\ServiceProvider
類。大多服務提供者都包含一個 register 和一個boot
方法。在register
方法中,你只需要將服務綁定到 register
方法中, 你只需要 將服務綁定到 服務容器。而不要嘗試在register
方法中注冊任何監聽器,路由,或者其他任何功能。
使用 Artisan 命令行工具,通過 make:provider
命令可以生成一個新的提供者:
php artisan make:provider RiakServiceProvider
如上所述,在 register
方法中,你只需要將服務綁定到 服務容器 中。而不要嘗試在 register
方法中注冊任何監聽器,路由,或者其他任何功能。否則,你可能會意外地使用到尚未加載的服務提供者提供的服務。
讓我們來看一個基礎的服務提供者。在任何服務提供者方法中,你總是通過 $app 屬性來訪問服務容器:
<?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'));
});
}
}
這個服務提供者只是定義了一個 register
方法,並且使用這個方法在服務容器中定義了一個 Riak\Connection
接口。如果你不理解服務容器的工作原理,請查看其 文檔.
如果你的服務提供器注冊了許多簡單的綁定,你可能想用 bindings
和 singletons
屬性替代手動注冊每個容器綁定。當服務提供器被框架加載時,將自動檢查這些屬性並注冊相應的綁定:
<?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
方法了。該方法在所有服務提供者被注冊以後才會被調用,這就是說我們可以在其中訪問框架已注冊的所有其它服務:
<?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
方法設置類型提示。服務容器 會自動注入你所需要的依賴:
use Illuminate\Contracts\Routing\ResponseFactory;
/**
* 引導所有的應用服務
*/
public function boot(ResponseFactory $response): void
{
$response->macro('serialized', function (mixed $value) {
// ...
});
}
所有服務提供者都是通過配置文件 config/app.php
進行注冊。該文件包含了一個列出所有服務提供者名字的 providers
數組,默認情況下,其中列出了所有核心服務提供者,這些服務提供者啟動 Laravel 核心組件,比如郵件、隊列、緩存等等。
要注冊提供器,只需要將其添加到數組:
'providers' => [
// 其他服務提供者
App\Providers\ComposerServiceProvider::class,
],
如果你的服務提供者 只 在 服務容器中注冊,可以選擇延遲加載該綁定直到注冊綁定的服務真的需要時再加載,延遲加載這樣的一個提供者將會提升應用的性能,因為它不會在每次請求時都從文件系統加載。
Laravel 編譯並保存延遲服務提供者提供的所有服務的列表,以及其服務提供者類的名稱。因此,只有當你在嘗試解析其中一項服務時,Laravel 才會加載服務提供者。
要延遲加載提供者,需要實現 \Illuminate\Contracts\Support\DeferrableProvider
接口並置一個 provides
方法。這個 provides
方法返回該提供者注冊的服務容器綁定:
<?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']);
});
}
/**
* 獲取服務提供者的服務
*
* @return array<int, string>
*/
public function provides(): array
{
return [Connection::class];
}
}