服務提供者
簡介
服務提供者是所有 Laravel 應用程式引導啟動的中心。你自己的應用程式以及所有 Laravel 的核心服務都是透過服務提供者引導啟動的。
但是,“引導啟動”(bootstrapped)是什麼意思呢?通常,我們指的是註冊,包括註冊服務容器繫結、事件監聽器、中介軟體,甚至是路由。服務提供者是配置應用程式的核心場所。
Laravel 在內部使用了數十個服務提供者來引導其核心服務,例如郵件傳送、佇列、快取等。其中許多提供者是“延遲”(deferred)提供者,這意味著它們不會在每個請求中載入,而僅在真正需要它們提供的服務時才會載入。
所有使用者定義的服務提供者都在 bootstrap/providers.php 檔案中註冊。在接下來的文件中,你將學習如何編寫自己的服務提供者並將其註冊到 Laravel 應用程式中。
如果你想深入瞭解 Laravel 如何處理請求及其內部工作原理,請檢視關於 Laravel 請求生命週期 的文件。
編寫服務提供者
所有服務提供者都繼承自 Illuminate\Support\ServiceProvider 類。大多數服務提供者包含一個 register 方法和一個 boot 方法。在 register 方法中,你應該只將內容繫結到 服務容器 中。你絕對不應該在 register 方法中嘗試註冊任何事件監聽器、路由或任何其他功能。
Artisan CLI 可以透過 make:provider 命令生成新的提供者。Laravel 會自動在應用程式的 bootstrap/providers.php 檔案中註冊你的新提供者。
1php artisan make:provider RiakServiceProvider
Register 方法
如前所述,在 register 方法中,你只能將內容繫結到 服務容器 中。你絕對不應該在 register 方法中嘗試註冊任何事件監聽器、路由或任何其他功能。否則,你可能會意外使用尚未載入的服務提供者所提供的服務。
讓我們看一個基本的服務提供者。在任何服務提供者方法中,你始終可以訪問 $app 屬性,該屬性提供了對服務容器的訪問許可權。
1<?php 2 3namespace App\Providers; 4 5use App\Services\Riak\Connection; 6use Illuminate\Contracts\Foundation\Application; 7use Illuminate\Support\ServiceProvider; 8 9class RiakServiceProvider extends ServiceProvider10{11 /**12 * Register any application services.13 */14 public function register(): void15 {16 $this->app->singleton(Connection::class, function (Application $app) {17 return new Connection(config('riak'));18 });19 }20}
此服務提供者僅定義了一個 register 方法,並使用該方法在服務容器中定義了 App\Services\Riak\Connection 的實現。如果你還不熟悉 Laravel 的服務容器,請查閱 相關文件。
bindings 和 singletons 屬性
如果你的服務提供者註冊了許多簡單的繫結,你可能希望使用 bindings 和 singletons 屬性,而不是手動註冊每個容器繫結。當框架載入該服務提供者時,它會自動檢查這些屬性並註冊相應的繫結。
1<?php 2 3namespace App\Providers; 4 5use App\Contracts\DowntimeNotifier; 6use App\Contracts\ServerProvider; 7use App\Services\DigitalOceanServerProvider; 8use App\Services\PingdomDowntimeNotifier; 9use App\Services\ServerToolsProvider;10use Illuminate\Support\ServiceProvider;11 12class AppServiceProvider extends ServiceProvider13{14 /**15 * All of the container bindings that should be registered.16 *17 * @var array18 */19 public $bindings = [20 ServerProvider::class => DigitalOceanServerProvider::class,21 ];22 23 /**24 * All of the container singletons that should be registered.25 *26 * @var array27 */28 public $singletons = [29 DowntimeNotifier::class => PingdomDowntimeNotifier::class,30 ServerProvider::class => ServerToolsProvider::class,31 ];32}
Boot 方法
那麼,如果我們需要在服務提供者中註冊一個 檢視合成器(view composer) 該怎麼辦?這應該在 boot 方法中完成。此方法在所有其他服務提供者都被註冊後呼叫,這意味著你可以訪問框架註冊的所有其他服務。
1<?php 2 3namespace App\Providers; 4 5use Illuminate\Support\Facades\View; 6use Illuminate\Support\ServiceProvider; 7 8class ComposerServiceProvider extends ServiceProvider 9{10 /**11 * Bootstrap any application services.12 */13 public function boot(): void14 {15 View::composer('view', function () {16 // ...17 });18 }19}
Boot 方法依賴注入
你可以為服務提供者的 boot 方法進行依賴注入型別提示。 服務容器 會自動注入你需要的任何依賴項。
1use Illuminate\Contracts\Routing\ResponseFactory; 2 3/** 4 * Bootstrap any application services. 5 */ 6public function boot(ResponseFactory $response): void 7{ 8 $response->macro('serialized', function (mixed $value) { 9 // ...10 });11}
註冊提供者
所有服務提供者都在 bootstrap/providers.php 配置檔案中註冊。該檔案返回一個數組,其中包含應用程式服務提供者的類名。
1<?php2 3return [4 App\Providers\AppServiceProvider::class,5];
當你呼叫 make:provider Artisan 命令時,Laravel 會自動將生成的提供者新增到 bootstrap/providers.php 檔案中。但是,如果你手動建立了提供者類,則應該手動將該提供者類新增到陣列中。
1<?php2 3return [4 App\Providers\AppServiceProvider::class,5 App\Providers\ComposerServiceProvider::class, 6];
延遲提供者
如果你的提供者僅在 服務容器 中註冊繫結,則可以選擇延遲其註冊,直到真正需要其中一個註冊的繫結時再進行載入。延遲載入此類提供者將提高應用程式的效能,因為它不會在每個請求中都從檔案系統載入。
Laravel 會編譯並存儲一份由延遲服務提供者提供的所有服務的列表,以及對應的服務提供者類名。然後,僅當你嘗試解析這些服務之一時,Laravel 才會載入該服務提供者。
要延遲提供者的載入,請實現 \Illuminate\Contracts\Support\DeferrableProvider 介面並定義一個 provides 方法。provides 方法應返回該提供者所註冊的服務容器繫結。
1<?php 2 3namespace App\Providers; 4 5use App\Services\Riak\Connection; 6use Illuminate\Contracts\Foundation\Application; 7use Illuminate\Contracts\Support\DeferrableProvider; 8use Illuminate\Support\ServiceProvider; 9 10class RiakServiceProvider extends ServiceProvider implements DeferrableProvider11{12 /**13 * Register any application services.14 */15 public function register(): void16 {17 $this->app->singleton(Connection::class, function (Application $app) {18 return new Connection($app['config']['riak']);19 });20 }21 22 /**23 * Get the services provided by the provider.24 *25 * @return array<int, string>26 */27 public function provides(): array28 {29 return [Connection::class];30 }31}