目錄php
你的應用程序以及 Laravel 的全部核心服務都是經過服務提供器進行引導(註冊),服務提供器是配置你的應用程序的中心。api
Laravel 的 config/app.php 文件中有一個 providers 數組。數組中的內容是應用程序要加載的全部服務提供器類。這其中有許多提供器並不會在每次請求時都被加載,只有當它們提供的服務實際須要時纔會加載。這種咱們稱之爲「延遲」提供器,推遲加載這種提供器會提升應用程序的性能。數組
生成文件:app
php artisan make:provider RiakServiceProvider
運行後會發現生成了app/Providers/RiakServiceProvider.php文件composer
生成文件內容:框架
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class RiakServiceProvider extends ServiceProvider { /** * Bootstrap the application services. * * @return void */ public function boot() { // } /** * Register the application services. * * @return void */ public function register() { // } }
從以上文件能夠看出:ide
只是單純的綁定,不註冊任什麼時候間的監聽器、路由、或者其它任何功能,不然你可能使用到未加載的服務memcached
在你的任何服務提供器方法中,你能夠經過 $app 屬性來訪問服務容器:函數
傳遞咱們想要註冊的類或接口名稱再返回類的實例的 Closure :性能
$this->app->bind('HelpSpot\API', function ($app) { return new HelpSpot\API($app->make('HttpClient')); });
singleton 方法將類或接口綁定到只能解析一次的容器中。綁定的單例被解析後,相同的對象實例會在隨後的調用中返回到容器中:
$this->app->singleton('HelpSpot\API', function ($app) { return new HelpSpot\API($app->make('HttpClient')); });
你也可使用 instance 方法將現有對象實例綁定到容器中。給定的實例會始終在隨後的調用中返回到容器中
$api = new HelpSpot\API(new HttpClient); $this->app->instance('HelpSpot\API', $api);
當你有一個類不只須要接受一個注入類,還須要注入一個基本值(好比整數)。你可使用上下文綁定來輕鬆注入你的類須要的任何值:
$this->app->when('App\Http\Controllers\UserController') ->needs('$variableName') ->give($value);
此方法在全部其它服務提供者都註冊以後才調用,咱們能夠訪問因此已經被框架註冊的服務,好比咱們想實現某個功能,好比中間件功能,事件監聽等,你甚至能夠在這裏註冊一條路由,這樣就能夠是這條路由生效了
public function boot() { view()->composer('view', function () { // }); }
能夠在boot方法使用依賴注入
use Illuminate\Contracts\Routing\ResponseFactory; //使用了類型提示 public function boot(ResponseFactory $response) { $response->macro('caps', function ($value) { // }); }
建立好服務提供者以後須要在config/app.php配置文件中註冊,寫在providers數組中。
'providers' => [ // 其餘服務提供器 App\Providers\ComposerServiceProvider::class, ],
你可使用 make 方法將容器中的類實例解析出來。make 方法接受要解析的類或接口的名稱:
$api = $this->app->make('HelpSpot\API');
若是你的代碼處於不能訪問 $app 變量的位置,你可使用全局的輔助函數 resolve:
$api = resolve('HelpSpot\API');
若是你的某些類的依賴項不能經過容器去解析,那你能夠經過將它們做爲關聯數組傳遞到 makeWith 方法來注入它們。
$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);
你能夠簡單地使用「類型提示」的方式在由容器解析的類的構造函數中添加依賴項,包括 控制器、事件監聽器、隊列任務、中間件 等。 事實上,這是你的大多數對象也應該由容器解析。
例如,你能夠在控制器的構造函數中對應用程序定義的 Repository 使用類型提示。Repository 會被自動解析並注入到類中:
<?php namespace App\Http\Controllers; use App\Users\Repository as UserRepository; class UserController extends Controller { /** * 用戶存儲庫實例。 */ protected $users; /** * 建立一個新的控制器實例。 * * @param UserRepository $users * @return void */ public function __construct(UserRepository $users) { $this->users = $users; } /** * 顯示指定 ID 的用戶信息。 * * @param int $id * @return Response */ public function show($id) { // } }
上面談到了註冊方法中的綁定,咱們要指到服務容器有一個強大的功能,就是將接口綁定到給定實現。
例如,若是咱們有一個 EventPusher 接口和一個 RedisEventPusher 實現。編寫完接口的 RedisEventPusher 實現後,咱們就能夠在服務容器中註冊它,像這樣:
$this->app->bind( 'App\Contracts\EventPusher', 'App\Services\RedisEventPusher' );
使用也很簡單,能夠在構造函數或者任何其餘經過服務容器注入依賴項的地方使用類型提示注入 EventPusher 接口:
use App\Contracts\EventPusher; /** * 建立一個新的類實例 * * @param EventPusher $pusher * @return void */ public function __construct(EventPusher $pusher) { $this->pusher = $pusher; }
分析以上的代碼,當一個類須要實現 EventPusher 時,應該注入 RedisEventPusher,咱們可能在更多的地方注入EventPusher,有一天咱們想換成memcachedEventPusher,再想一想,是否是發現了,咱們沒必要去一個個修改每個使用EventPusher的類,咱們只須要在服務提供器這裏修改就能夠了。
$this->app->bind( 'App\Contracts\EventPusher', 'App\Services\memcachedEventPusher' );
有時候,你可能有兩個類使用了相同的接口,但你但願每一個類都能注入不一樣的實現。例如,兩個控制器可能須要依賴不一樣的 Illuminate\Contracts\Filesystem\Filesystem 契約 實現。 Laravel 提供了一個簡單、優雅的接口來定義這個行爲:
use Illuminate\Support\Facades\Storage; use App\Http\Controllers\PhotoController; use App\Http\Controllers\VideoController; use Illuminate\Contracts\Filesystem\Filesystem; $this->app->when(PhotoController::class) ->needs(Filesystem::class) ->give(function () { return Storage::disk('local'); }); $this->app->when(VideoController::class) ->needs(Filesystem::class) ->give(function () { return Storage::disk('s3'); });
標記#
有時候,你可能須要解析某個「分類」下的全部綁定。例如,你正在構建一個報表的聚合器,它接收一個包含不一樣 Report 接口實現的數組。註冊了 Report 實現後,你可使用 tag 方法爲其分配標籤:
$this->app->bind('SpeedReport', function () { // }); $this->app->bind('MemoryReport', function () { // }); $this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');
服務被標記後,你能夠經過 tagged 方法輕鬆地將它們所有解析:
$this->app->bind('ReportAggregator', function ($app) { return new ReportAggregator($app->tagged('reports')); });
推遲其註冊,提升性能,只有在嘗試解析其中的服務時候纔會加載服務提供者。
要延遲提供器的加載,請將 defer 屬性設置爲 true ,並定義 provides 方法。provides 方法應該返回由提供器註冊的服務容器綁定:
<?php namespace App\Providers; use Riak\Connection; use Illuminate\Support\ServiceProvider; class RiakServiceProvider extends ServiceProvider { //1.是否延時加載服務提供者 protected $defer = true; public function register() { $this->app->singleton(Connection::class, function ($app) { return new Connection($app['config']['riak']); }); } //2.定義provides方法,返回由提供器註冊的服務容器綁定 public function provides() { return [Connection::class]; } }
在給消費者使用前,能夠作最後一步監聽修改(Container Events)
每當服務容器解析一個對象時觸發一個事件。你可使用 resolving 方法監聽這個事件:
$this->app->resolving(function ($object, $app) { // 當容器解析任何類型的對象時調用... }); $this->app->resolving(HelpSpot\API::class, function ($api, $app) { // 當容器解析類型爲「HelpSpot\API」的對象時調用... });
如你所見,被解析的對象會被傳遞給回調中,讓你在對象被傳遞出去以前能夠在對象上設置任何屬性。