服務(wù)提供者
服務(wù)提供者
服務(wù)提供者
簡介
服務(wù)提供者是所有 Laravel 應(yīng)用程序的引導(dǎo)中心。你的應(yīng)用程序,以及 通過服務(wù)器引導(dǎo)的 Laravel 核心服務(wù)都是通過服務(wù)提供器引導(dǎo)。
但是,「引導(dǎo)」是什么意思呢? 通常,我們的可以理解為注冊(cè),比如注冊(cè)服務(wù)容器綁定,事件監(jiān)聽器,中間件,甚至是路由。服務(wù)提供者是配置應(yīng)用程序的中心。
當(dāng)你打開 Laravel 的 config/app.php
文件時(shí),你會(huì)看到 providers
數(shù)組。數(shù)組中的內(nèi)容是應(yīng)用程序要加載的所有服務(wù)提供者的類。當(dāng)然,其中有很多 「延遲」 提供者,他們并不會(huì)在每次請(qǐng)求的時(shí)候都加載,只有他們的服務(wù)實(shí)際被需要時(shí)才會(huì)加載。
本篇你將會(huì)學(xué)到如何編寫自己的服務(wù)提供者,并將其注冊(cè)到你的 Laravel 應(yīng)用程序中
編寫服務(wù)提供者
所有的服務(wù)提供者都會(huì)繼承 Illuminate\Support\ServiceProvider
類。 大多服務(wù)提供者都包含一個(gè) register
和一個(gè) boot
方法。在 register
方法中, 你只需要將事物綁定到服務(wù)容器。而不要嘗試在 register
方法中注冊(cè)任何監(jiān)聽器,路由,或者其他任何功能
使用 Artisan 命令行工具,通過 make:provider
命令可以生成一個(gè)新的提供者:
php artisan make:provider RiakServiceProvider
注冊(cè)方法
如上所述,在 register
方法中,你只需要將服務(wù)綁定到服務(wù)容器中。而不要嘗試在 register
方法中注冊(cè)任何監(jiān)聽器,路由,或者其他任何功能。否則,你可能會(huì)意外地使用到尚未加載的服務(wù)提供者提供的服務(wù)。
讓我們來看一個(gè)基礎(chǔ)的服務(wù)提供者。在任何服務(wù)提供者方法中,你總是通過 $app
屬性來訪問服務(wù)容器:
<?php namespace App\Providers;use Riak\Connection; use Illuminate\Support\ServiceProvider; class RiakServiceProvider extends ServiceProvider{ /** * 在服務(wù)容器里注冊(cè) * * @return void */ public function register() { $this->app->singleton(Connection::class, function ($app) { return new Connection(config('riak')); }); } }
這個(gè)服務(wù)容器只是定義了一個(gè) register
方法,并且使用該這個(gè)方法在服務(wù)容器中定義了一個(gè) Riak\Connection
接口。如果你不理解服務(wù)容器的工作原理,請(qǐng)查看其文檔。
bindings
和 singletons
的特性
如果你的服務(wù)提供器注冊(cè)了許多簡單的綁定,你可能想用 bindings
和 singletons
屬性替代手動(dòng)注冊(cè)每個(gè)容器綁定。當(dāng)服務(wù)提供器被框架加載時(shí),將自動(dòng)檢查這些屬性并注冊(cè)相應(yīng)的綁定
<?php namespace App\Providers; use App\Contracts\ServerProvider; use App\Contracts\DowntimeNotifier; use Illuminate\Support\ServiceProvider; use App\Services\PingdomDowntimeNotifier; use App\Services\DigitalOceanServerProvider; class AppServiceProvider extends ServiceProvider{ /** * 設(shè)定所有的容器綁定的對(duì)應(yīng)關(guān)系 * * @var array */ public $bindings = [ ServerProvider::class => DigitalOceanServerProvider::class, ]; /** * 設(shè)定所有的單例模式容器綁定的對(duì)應(yīng)關(guān)系 * * @var array */ public $singletons = [ DowntimeNotifier::class => PingdomDowntimeNotifier::class, ]; }
引導(dǎo)方法
如果我們要在服務(wù)提供者中注冊(cè)一個(gè) 視圖合成器 該怎么做? 這就需要用到 boot
方法了。 該方法在所有服務(wù)提供者被注冊(cè)以后才會(huì)被調(diào)用, 這就是說我們可以在其中訪問框架已注冊(cè)的所有其它服務(wù):
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class ComposerServiceProvider extends ServiceProvider{ /** * 啟動(dòng)所有的應(yīng)用服務(wù)。 * * @return void */ public function boot() { view()->composer('view', function () { // }); } }
啟動(dòng)方法的依賴注入
你可以為服務(wù)提供者的 boot
方法設(shè)置類型提示。 服務(wù)容器 會(huì)自動(dòng)注入你所需要的依賴:
use Illuminate\Contracts\Routing\ResponseFactory; public function boot(ResponseFactory $response){ $response->macro('caps', function ($value) { // }); }
注冊(cè)服務(wù)提供者
所有服務(wù)提供者都是通過配置文件 config/app.php
進(jìn)行注冊(cè)。該文件包含了一個(gè)列出所有服務(wù)提供者名字的 providers
數(shù)組,默認(rèn)情況下,其中列出了所有核心服務(wù)提供者,這些服務(wù)提供者啟動(dòng) Laravel 核心組件,比如郵件、隊(duì)列、緩存等等。
要注冊(cè)提供器,只需要將其添加到數(shù)組:
'providers' => [ // 其他服務(wù)提供者 App\Providers\ComposerServiceProvider::class,],
延遲提供者
如果你的服務(wù)提供者 只 在 服務(wù)容器 中注冊(cè),可以選擇延遲加載該綁定直到注冊(cè)綁定的服務(wù)真的需要時(shí)再加載,延遲加載這樣的一個(gè)提供者將會(huì)提升應(yīng)用的性能,因?yàn)樗粫?huì)在每次請(qǐng)求時(shí)都從文件系統(tǒng)加載。
Laravel 編譯并保存延遲服務(wù)提供者提供的所有服務(wù)的列表,以及其服務(wù)提供者類的名稱。因此,只有當(dāng)你在嘗試解析其中一項(xiàng)服務(wù)時(shí),Laravel 才會(huì)加載服務(wù)提供者。
要延遲加載提供者,需要實(shí)現(xiàn) \Illuminate\Contracts\Support\DeferrableProvider
接口并置一個(gè) provides
方法。這個(gè) provides
方法返回該提供者注冊(cè)的服務(wù)容器綁定:
<?php namespace App\Providers; use Riak\Connection; use Illuminate\Support\ServiceProvider; use Illuminate\Contracts\Support\DeferrableProvider; class RiakServiceProvider extends ServiceProvider implements DeferrableProvider{ /** * 注冊(cè)服務(wù)提供者。 * * @return void */ public function register() { $this->app->singleton(Connection::class, function ($app) { return new Connection($app['config']['riak']); }); } /** * 獲取由提供者提供的服務(wù)。 * * @return array */ public function provides() { return [Connection::class]; } }