什麼是Service Container
The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection.
從Laravel官方文件對於Service Container的解釋可以看出,它的作用就是幫助我們管理和進行依賴注入的。
為什麼要用Service Container
在《依賴注入》中,我們看到使用依賴注入可以極大的降低程式碼的耦合度,但是也帶來了一個缺點,就是需要自己管理注入的對象。
如果一個元件有很多依賴,我們需要創建多個參數的setter方法來傳遞依賴關係,或者建立一個多個參數的建構函數來傳遞它們,另外在使用元件前還要每次都創建依賴,這讓我們的程式碼像這樣不易維護。
所以為依賴實例提供一個容器(Service Container),就是一個實用且優雅的方法。
例如下面是laravel的入口檔案(已去掉註解):
// 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
,其中$app
是 Illuminate\Foundation\Application
的一個實例,而Illuminate\Foundation\Application
類別繼承自Container,所以$app
其實就是一個Service Container。
然後下面的三個singleton方法定義了當依賴Illuminate\Contracts\Http\Kernel
、Illuminate\Contracts\Console\Kernel
、Illuminate\Contracts\Debug \ExceptionHandler
這三個介面時,注入哪個類別的單例。
然後看public/index.php
,其中的make方法其實就是用Service Container來new一個Illuminate\Contracts\Http\Kernel
實例,跟普通new的差別就是會把他的依賴自動注入進去。
是不是很簡潔?
其實不單是Laravel,像是Yii2、Phalcon等框架都透過實作容器來管理依賴注入。
如何使用Service Container
既然是一個容器,無非就是兩個事,往裡面放東西和往外取東西,對應到Service Container就是綁定(Binding)和解析(Resolving )。
取得容器
在Laravel的Service Provider中,可以透過$this->app
取得容器,除此之外,還可以使用app ()
來取得容器。
如果在Laravel外使用Service Container,直接new一個Illuminate\Container\Container
就可以獲得容器了。
以下都用$container代表取得到的容器。
綁定
綁定傳回介面的實例
//使用闭包 $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 $inpidual) { $inpidual->doSomething(); }
解析事件
每當服務容器解析一個物件時就會觸發事件。你可以使用 resolving 方法監聽這個事件。
$container->resolving(function ($object, $container) { // 当容器解析任何类型的对象时会被调用... }); $container->resolving('Foo', function (Foo $foo, $container) { // 当容器解析「Foo」类型的对象时会被调用... });
相關推薦:最新的五個Laravel影片教學