[Laravel]嘮嘮Service Container

什麼是Service Container

The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection.php

從Laravel官方文檔對於Service Container的解釋能夠看出,它的做用就是幫助咱們管理和進行依賴注入的。laravel

爲何要用Service Container

《嘮嘮依賴注入》中,咱們看到使用依賴注入能夠極大的下降代碼的耦合度,可是也帶來了一個缺點,就是須要本身管理注入的對象。
若是一個組件有不少依賴,咱們須要建立多個參數的setter方法​​來傳遞依賴關係,或者創建一個多個參數的構造函數來傳遞它們,另外在使用組件前還要每次都建立依賴,這讓咱們的代碼像這樣不易維護。
因此爲依賴實例提供一個容器(Service Container),就是一個實用並且優雅的方法。
好比下面是laravel的入口文件(已去掉註釋):git

// public/index.php
<?php

require __DIR__.'/../bootstrap/autoload.php';

$app = require_once __DIR__.'/../bootstrap/app.php';

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

$response->send();

$kernel->terminate($request, $response);
// bootstrap/app.php
<?php

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

return $app;

首先看bootstrap/app.php,其中$appIlluminate\Foundation\Application的一個實例,而Illuminate\Foundation\Application類繼承自Container,因此$app實際上就是一個Service Container。
而後下面的三個singleton方法定義了當依賴Illuminate\Contracts\Http\KernelIlluminate\Contracts\Console\KernelIlluminate\Contracts\Debug\ExceptionHandler這三個接口時,注入哪一個類的單例。
而後看public/index.php,其中的make方法實際上就是用Service Container來new一個Illuminate\Contracts\Http\Kernel實例,跟普通new的區別就是會把他的依賴自動注入進去。github

是否是很簡潔?bootstrap

其實不單是Laravel,像Yii二、Phalcon等框架都經過實現容器來管理依賴注入。數組

如何使用Service Container

既然是一個容器,無非就是兩個事,往裏放東西和往外取東西,對應到Service Container就是綁定(Binding)和解析(Resolving)。閉包

得到容器

在Laravel的Service Provider中,能夠經過$this->app獲取容器,除此以外,還可使用app()來獲取容器。
若是在Laravel外使用Service Container,直接new一個Illuminate\Container\Container就能夠得到容器了。app

如下都用$container表明獲取到的容器。框架

綁定

  • 綁定返回接口的實例ide

//使用閉包
$container->bind('BarInterface', function(){
    return new Bar();
});
//或者使用字符串
$container->bind('FooInterface', 'Foo');
  • 綁定單例

singletion 方法綁定一個只會被解析一次的類或接口至容器中,且後面的調用都會從容器中返回相同的實例:

$container->singleton('BarInterface', function(){
    return new Bar();
});
  • 綁定實例

你也可使用 instance 方法,綁定一個已經存在的對象實例至容器中。後面的調用都會從容器中返回指定的實例:

$bar = new Bar();
$bar->setSomething(new Something);

$container->instance('Bar', $bar);
  • 情境綁定

有時候,你可能有兩個類使用到相同接口,但你但願每一個類都能注入不一樣實現。

$container->when('Man')
          ->needs('PartnerInterface')
          ->give('Woman');
$container->when('Woman')
          ->needs('PartnerInterface')
          ->give('Man');
  • 標記

有些時候,可能須要解析某個「分類」下的全部綁定。

$container->bind('Father', function () {
    //
});
$container->bind('Mother', function () {
    //
});
$container->bind('Daughter', function () {
    //
});
$container->bind('Son', function () {
    //
});
$container->tag(['Father', 'Mother', 'Daughter', 'Son'], 'familyMembers');

$container->bind('Family', function ($container) {
    return new Family($container->tagged('familyMembers'));
});

解析

  • make方法

$foo = $container->make('Foo');
  • 數組方法

$bar = $container['Bar'];
  • 解析被標記綁定

$familyMembers = $container->tagged('familyMembers');

foreach ($familyMembers as $individual) {
    $individual->doSomething();
}

解析事件

每當服務容器解析一個對象時就會觸發事件。你可使用 resolving 方法監聽這個事件。

$container->resolving(function ($object, $container) {
    // 當容器解析任何類型的對象時會被調用...
});

$container->resolving('Foo', function (Foo $foo, $container) {
    // 當容器解析「Foo」類型的對象時會被調用...
});

博客地址:http://haitian299.github.io/2016/05/17/laravel-service-container/

相關文章
相關標籤/搜索