摘要:下邊是服務(wù)容器工作示意圖服務(wù)容器的產(chǎn)生框架中,服務(wù)容器是由中類完成的,該類實現(xiàn)了服務(wù)容器的核心功能。并不是框架中所有的類都能實現(xiàn)自動依賴注入的功能只有服務(wù)容器創(chuàng)建的類實例才能實現(xiàn)依賴自動注入。框架中的服務(wù)容器是全局的,不需要
1.服務(wù)容器
“服務(wù)容器”是Lumen框架整個系統(tǒng)功能調(diào)度配置的核心,它提供了整個框架運行過程中的一系列服務(wù)。“服務(wù)容器”就是提供服務(wù)(服務(wù)可以理解為系統(tǒng)運行中需要的東西,如:對象、文件路徑、系統(tǒng)配置等)的載體,在系統(tǒng)運行的過程中動態(tài)的為系統(tǒng)提供這些服務(wù)。下邊是服務(wù)容器工作示意圖:
1.1、服務(wù)容器的產(chǎn)生Lumen框架中,服務(wù)容器是由illuminate/container/Container.php中Container類完成的,該類實現(xiàn)了服務(wù)容器的核心功能。laravel/lumen-framework/src/Application.php中Application類繼承了該類,實現(xiàn)了服務(wù)容器初始化配置和功能拓展。源碼中生成服務(wù)容器的代碼是在bootstrap/app.php中:
$app = new LaravelLumenApplication( dirname(__DIR__) );
也就是Lumen框架在處理每一個請求的時候,都會首先為這個請求生成一個服務(wù)容器,用于容納請求處理需要的服務(wù)。
1.2、服務(wù)綁定服務(wù)容器生成以后,就可以向其中添加服務(wù),服務(wù)綁定可以理解為一個服務(wù)和一個關(guān)鍵字綁定,看作鍵值對的形式就是:一個"key" 對應(yīng)一個服務(wù)。要綁定的服務(wù)不同,使用的容器中的綁定函數(shù)也不同,框架初始化時使用到的是回調(diào)函數(shù)服務(wù)綁定和實例對象服務(wù)綁定。回調(diào)函數(shù)綁定分兩種:一種是普通綁定,另外一種是單例綁定,通過bind()函數(shù)中的參數(shù)$shared進(jìn)行區(qū)分,項目代碼中的singleton()綁定單例就是bind()函數(shù)中$shared參數(shù)為true的情況。源碼如下:
public function singleton($abstract, $concrete = null) { $this->bind($abstract, $concrete, true); }
單例綁定在整個Lumen生命周期中只會生成并使用一個實例對象。綁定一個實例對象到容器中使用的是instance()函數(shù),綁定之后生成的實例對象會在$instance屬性中記錄。回調(diào)函數(shù)的綁定在$bindings屬性中記錄。
有一種情況是綁定具體類名稱,實際上也是綁定回調(diào)函數(shù)的方式,只是回調(diào)函數(shù)是服務(wù)容器根據(jù)提供的參數(shù)自動生成的,下面章節(jié)我們會詳細(xì)講解。源碼中有如下代碼:
$app->singleton( IlluminateContractsDebugExceptionHandler::class, AppExceptionsHandler::class ); $app->singleton( IlluminateContractsConsoleKernel::class, AppConsoleKernel::class );
在服務(wù)綁定過程中,盡量使用接口名稱和服務(wù)進(jìn)行綁定,這樣可以使得一個具體的功能僅僅和接口實現(xiàn)了耦合,當(dāng)應(yīng)用需求變化時可以修改具體類,只要這個類還符合接口規(guī)范,程序依然可以健壯的運行。這種“面向接口”編程是一種新的,更有效的解決依賴的編程模式。Lumen框架的接口定義規(guī)范都放在/learnLumen/vendor/illuminate/contracts 文件夾下。
1.3、服務(wù)解析服務(wù)綁定到容器之后,運行程序就可以隨時從容器中取出服務(wù),這個過程稱為“服務(wù)解析”。服務(wù)解析的步驟就是運行程序先獲取到容器對象,然后使用容器對象解析相應(yīng)的服務(wù)。服務(wù)解析有常用幾種方式:
使用保存服務(wù)容器成員屬性,調(diào)用make函數(shù)進(jìn)行解析
$this->app->make(AppServiceExampleService::class);
通過全局函數(shù)app()來獲取
app(AppServiceExampleService::class);
如果程序使用了Facades外觀,還可以通過靜態(tài)方法來解析
App::make(AppServiceExampleService::class);
服務(wù)容器類Container實現(xiàn)了ArrayAccess接口,可以使用數(shù)組的方式進(jìn)行服務(wù)解析
app[AppServiceExampleService::class];
ArrayAccess(數(shù)組式訪問)接口非常有用,提供了像訪問數(shù)組一樣訪問對象的能力的接口。
使用依賴注入的方式也可以實現(xiàn)服務(wù)的自動解析。即在類的構(gòu)造函數(shù)中,使用相應(yīng)的類提示符,容器會利用自身的反射機(jī)制自動解析依賴并實現(xiàn)注入。需要注意的是:在服務(wù)注冊以后使用依賴注入功能,則該服務(wù)名稱和服務(wù)是要遵循一定規(guī)范的。即服務(wù)名稱一般為服務(wù)生成的類名稱或者接口名稱,只有這樣當(dāng)服務(wù)根據(jù)依賴限制查找到服務(wù)后生成的實例對象才能滿足這個限制,否則就會報錯。
并不是Lumen框架中所有的類都能實現(xiàn)自動依賴注入的功能,只有“服務(wù)容器”創(chuàng)建的類實例才能實現(xiàn)依賴自動注入。
2.控制反轉(zhuǎn)(Ioc)和依賴注入(DI)控制反轉(zhuǎn)是框架設(shè)計的一種原則,在很大程度上降低了代碼模塊之間的耦合度,有利于框架維護(hù)和拓展。實現(xiàn)控制反轉(zhuǎn)最常見的方法是“依賴注入”,還有一種方法叫“依賴查找”。控制反轉(zhuǎn)將框架中解決依賴的邏輯從實現(xiàn)代碼類庫的內(nèi)部提取到了外部來管理實現(xiàn)。
我們用簡單代碼模擬一下Lumen處理用戶請求的邏輯,框架中要使用到最簡單的Request請求模塊、Response請求模塊,我們使用單例模式簡單實現(xiàn)一下:
//Request模塊實現(xiàn) class Request { static private $instance = null; private function __construct() { } private function __clone() { } static function getInstance() { if (self::$instance == null) self::$instance = new self(); return self::$instance; } public function get($key) { return $_GET[$key] ? $_GET[$key] : ""; } public function post($key) { return $_POST[$key] ? $_POST[$key] : ""; } } //Response模塊實現(xiàn) class Response { static private $instance = null; private function __construct() { } private function __clone() { } static function getInstance() { if (self::$instance == null) self::$instance = new self(); return self::$instance; } public function json($data) { return json_encode($data); } }
我們先來使用“依賴查找”的工廠模式來實現(xiàn)控制反轉(zhuǎn),我們需要一個工廠,簡單實現(xiàn)一下:
include_once "Request.php"; include_once "Response.php"; include_once "ExceptionHandler.php"; abstract class Factory { static function Create($type, array $params = []) { //根據(jù)接收到的參數(shù)確定要生產(chǎn)的對象 switch ($type) { case "request": return Request::getInstance(); break; case "response": return Response::getInstance(); break; case "exception": return new ExceptionHandler(); break; } } }
接下來就開始實現(xiàn)用戶邏輯,我們首先加入錯誤處理的簡單實現(xiàn):
//開啟報告代碼中的錯誤處理 class ExceptionHandler { public function __construct() { error_reporting(-1); ini_set("display_errors", true); } }
我們模擬一個請求用戶列表的邏輯:
include_once "Factory.php"; Factory::Create("exception"); //用戶邏輯 class UserLogic { private $modules = []; public function __construct(array $modules) { foreach ($modules as $key => $module) { $this->modules[$key] = Factory::Create($module); } } public function getUserList() { if ($this->modules["request"]->get("path") == "userlist") { $userList = [ ["name" => "張三", "age" => 18], ["name" => "李四", "age" => 22] ]; return $this->modules["response"]->json($userList); } } } try { $userLogic = new UserLogic(["request" => "request", "response" => "response"]); echo $userLogic->getUserList(); } catch (Error $e) { var_dump($e); exit(); }
可以看到我們使用工廠模式管理依賴的時候,可以在處理業(yè)務(wù)邏輯外部根據(jù)處理請求需要依賴的模塊自行進(jìn)行注入。比如例子中就注入了request、response模塊。這種模式雖然解決了我們處理邏輯對外部模塊的依賴管理問題,但是并不是太完美,我們的程序只是將原來邏輯對一個個實例子對象的依賴轉(zhuǎn)換成了工廠對這些實例子對象的依賴,工廠和這些實例子對象之間的耦合還存在,隨著工廠越來越大,用戶邏輯實現(xiàn)越來越復(fù)雜,這種“依賴查找”實現(xiàn)控制反轉(zhuǎn)的模式對于用戶來講依然很痛苦。
接下來我們使用Ioc服務(wù)容器來實現(xiàn)依賴注入,下邊先實現(xiàn)一個簡單的服務(wù)容器:
class Container { //用于裝提供實例的回調(diào)函數(shù),真正的容器還會裝實例等其他內(nèi)容 protected $bindings = []; //容器共享實例數(shù)組(單例) protected $instances = []; public function bind($abstract, $concrete = null, $shared = false) { if (! $concrete instanceof Closure) { //如果提供的參數(shù)不是回調(diào)函數(shù),則產(chǎn)生默認(rèn)的回調(diào)函數(shù) $concrete = $this->getClosure($abstract, $concrete); } $this->bindings[$abstract] = compact("concrete", "shared"); } public function getBuildings() { return $this->bindings; } //默認(rèn)生成實例的回調(diào)函數(shù) protected function getClosure($abstract, $concrete) { return function ($c) use ($abstract, $concrete) { $method = ($abstract == $concrete) ? "build" : "make"; //調(diào)用的是容器的build或make方法生成實例 return $c->$method($concrete); }; } //生成實例對象,首先解決接口和要實例化類之間的依賴關(guān)系 public function make($abstract) { $concrete = $this->getConcrete($abstract); if ($this->isBuildable($concrete, $abstract)) { $object = $this->build($this->build($concrete)); } else { $object = $this->make($concrete); } return $object; } protected function isBuildable($concrete, $abstract) { return $concrete === $abstract || $concrete instanceof Closure; } //獲取綁定的回調(diào)函數(shù) protected function getConcrete($abstract) { if (!isset($this->bindings[$abstract])) { return $abstract; } return $this->bindings[$abstract]["concrete"]; } //實例化一個對象 public function build($concrete) { if ($concrete instanceof Closure) { return $concrete($this); } $reflector = new ReflectionClass($concrete); if (! $reflector->isInstantiable()) { echo $message = "Target [$concrete] is not instantiable."; } $constructor = $reflector->getConstructor(); if(is_null($constructor)) { return new $concrete; } $dependencies = $constructor->getParameters(); $instances = $this->getDependencies($dependencies); return $reflector->newInstanceArgs($instances); } //通過反射機(jī)制實例化對象時的依賴 protected function getDependencies($parameters) { $dependencies = []; foreach($parameters as $parameter) { $dependency = $parameter->getClass(); if(is_null($dependency)) { $dependencies[] = NULL; } else { $dependencies[] = $this->resolveClass($parameter); } } return (array) $dependencies; } protected function resolveClass(ReflectionParameter $parameter) { return $this->make($parameter->getClass()->name); } //注冊一個實例并綁定到容器中 public function singleton($abstract, $concrete = null){ $this->bind($abstract, $concrete, true); } }
該服務(wù)容器可以稱為Lumen服務(wù)容器的簡化版,但是它實現(xiàn)的功能和Lumen服務(wù)容器是一樣的,雖然只有一百多行的代碼,但是理解起來有難度,這里就詳細(xì)講解清楚簡化版容器的代碼和原理,接下來章節(jié)對Lumen服務(wù)容器源碼分析時就僅僅只對方法做簡單介紹。
根據(jù)對服務(wù)容器介紹章節(jié)所講:容器中有兩個關(guān)鍵屬性$bindings和$instance,其中$bindings中存在加入到容器中的回調(diào)函數(shù),而$instance存放的是容器中綁定的實例對象。我們還知道$singleton方法用來綁定單例對象,其底層只是調(diào)用了bind方法而已,只不過$shared屬性為true,意為容器中全局共享:
//注冊一個實例并綁定到容器中 public function singleton($abstract, $concrete = null){ $this->bind($abstract, $concrete, true); }
bind方法的實現(xiàn)也很簡單,只是將用戶指定的服務(wù)解析好之后存放入相應(yīng)的屬性當(dāng)中:
public function bind($abstract, $concrete = null, $shared = false) { if (! $concrete instanceof Closure) { //如果提供的參數(shù)不是回調(diào)函數(shù),則產(chǎn)生默認(rèn)的回調(diào)函數(shù) $concrete = $this->getClosure($abstract, $concrete); } $this->bindings[$abstract] = compact("concrete", "shared"); }
Closure是php中的匿名函數(shù)類類型。$abstract和$concrete可以抽象理解為KV鍵值對,K就是$abstract,是服務(wù)名;V是$concrete,是服務(wù)的具體實現(xiàn)。我們理解容器,首先要將思維從平常的業(yè)務(wù)邏輯代碼中轉(zhuǎn)換回來。業(yè)務(wù)邏輯中操作的一般是用戶數(shù)據(jù),而容器中,我們操作的是對象、類、接口之類的,在框架中可稱為“服務(wù)”。如果用戶要綁定的具體實現(xiàn)$concrete不是匿名函數(shù),則調(diào)用getClosure方法生成一個匿名函數(shù):
//獲取綁定的回調(diào)函數(shù) //默認(rèn)生成實例的回調(diào)函數(shù) protected function getClosure($abstract, $concrete) { return function ($c) use ($abstract, $concrete) { $method = ($abstract == $concrete) ? "build" : "make"; //調(diào)用的是容器的build或make方法生成實例 return $c->$method($concrete); }; }
getClosure是根據(jù)用戶傳入的參數(shù)來決定調(diào)用系統(tǒng)的build和make方法。其中build方法就是構(gòu)建匿名函數(shù)和類實例的關(guān)鍵實現(xiàn),使用了php中的反射機(jī)制,解析出類實例:
//實例化一個對象 public function build($concrete) { if ($concrete instanceof Closure) { return $concrete($this); } $reflector = new ReflectionClass($concrete); if (! $reflector->isInstantiable()) { echo $message = "Target [$concrete] is not instantiable."; } $constructor = $reflector->getConstructor(); if(is_null($constructor)) { return new $concrete; } $dependencies = $constructor->getParameters(); $instances = $this->getDependencies($dependencies); return $reflector->newInstanceArgs($instances); }
build首先判斷參數(shù)$concrete是一個匿名函數(shù),就返回調(diào)用匿名函數(shù)的一個閉包。否則$concrete是一個類,利用反射機(jī)制解析類的信息,首先判斷類是否能夠被實例化(例如單例就不能被實例化,容器中的單例是通過屬性$shared來區(qū)分的);確保了類能夠被實例化以后,使用getConstructor()判斷類是否定義了構(gòu)造函數(shù),如果沒有定義構(gòu)造函數(shù),直接實例化得到一個類的實例。否則就再次調(diào)用getParameters獲取構(gòu)造函數(shù)中都傳入了哪些參數(shù)(也就是判斷$concrete類都有哪些依賴),getDependencies方法就是來生成$concrete依賴的函數(shù):
//通過反射機(jī)制實例化對象時的依賴 protected function getDependencies($parameters) { $dependencies = []; foreach($parameters as $parameter) { $dependency = $parameter->getClass(); if(is_null($dependency)) { $dependencies[] = NULL; } else { $dependencies[] = $this->resolveClass($parameter); } } return (array) $dependencies; }
得到了類依賴的實例以后,就調(diào)用newInstanceArgs($instances)來生成類的實例。
服務(wù)解析函數(shù)make主要由build函數(shù)實現(xiàn):
//生成實例對象,首先解決接口和要實例化類之間的依賴關(guān)系 public function make($abstract) { $concrete = $this->getConcrete($abstract); if ($this->isBuildable($concrete, $abstract)) { $object = $this->build($this->build($concrete)); } else { $object = $this->make($concrete); } return $object; }
有了服務(wù)容器以后,我們就可以使用服務(wù)容器來存儲處理請求中需要的服務(wù),并實現(xiàn)服務(wù)中的依賴自動注入。不過首先我們需要將Request、Response單例做修改,因為服務(wù)容器對單例的管理,是通過$shared屬性進(jìn)行設(shè)置的。所以Request、Response要能夠被實例化,才能保存到容器的$bindings數(shù)組中:
class Request { public function __construct() { } public function get($key) { return $_GET[$key] ? $_GET[$key] : ""; } public function post($key) { return $_POST[$key] ? $_POST[$key] : ""; } } class Response { public function __construct() { } public function json($data) { return json_encode($data); } }
我們再來看使用容器后處理用戶請求的源代碼:
include_once "Container.php"; include_once "Request.php"; include_once "Response.php"; include_once "ExceptionHandler.php"; $app = new Container(); //綁定錯誤處理 $app->bind("exception", "ExceptionHandler"); //將請求、響應(yīng)單例組件添加到容器中 $app->singleton("request", "Request"); $app->singleton("response", "Response"); //解析錯誤處理 $app->make("exception"); //用戶邏輯 class UserLogic { public $app = null; public function __construct(Container $app) { $this->app = $app; } public function getUserList() { if ($this->app->make("request")->get("path") == "userlist") { $userList = [ ["name" => "張三", "age" => 18], ["name" => "李四", "age" => 22] ]; return $this->app->make("response")->json($userList); } } } try { $userLogic = new UserLogic($app); echo $userLogic->getUserList(); } catch (Error $e) { var_dump($e); exit(); }
我們還是按照之前的步驟,使用容器將錯誤處理類綁定到容器中,然后解析出來使用。使用singleton方法將Request和Response類綁定到容器中,類型是單例。這樣我們管理服務(wù)模塊、實現(xiàn)依賴注入這些問題全都交給容器來做就好了。我們想要什么樣的服務(wù),就向容器中添加,在需要使用的時候,就利用容器解析使用就可以了。lumen框架中的服務(wù)容器是全局的,不需要像例子中一樣,手動注入到邏輯代碼中使用。
3.源碼解析對于lumen框架來講,服務(wù)容器相當(dāng)于發(fā)動機(jī),綁定與解析框架啟動和運行生命周期中所有的服務(wù)。它的大致架構(gòu)如下所示:
3.1、服務(wù)容器綁定的方法bind綁定
bindif綁定
singleton綁定
instance綁定
context綁定
數(shù)組綁定
標(biāo)簽綁定
extend拓展
Rebounds與Rebinding
源碼中bind實現(xiàn)代碼如下:
public function bind($abstract, $concrete = null, $shared = false) { $this->dropStaleInstances($abstract); if (is_null($concrete)) { $concrete = $abstract; } if (! $concrete instanceof Closure) { $concrete = $this->getClosure($abstract, $concrete); } $this->bindings[$abstract] = compact("concrete", "shared"); if ($this->resolved($abstract)) { $this->rebound($abstract); } }
從源碼中我們可知:使用bind方法綁定服務(wù),每次都會重新進(jìn)行綁定(刪除原來的綁定,再重新綁定)。我們類比服務(wù)容器中服務(wù)的綁定為KV健值對。key為接口名稱,而value為具體的服務(wù)實現(xiàn),之所以推薦使用接口名稱作為key,是因為只要開發(fā)者遵循相關(guān)的接口約束規(guī)范,就可以對服務(wù)進(jìn)行拓展和改進(jìn),這也是面向接口編程比較新穎之處。另外我們可以看到bind方法核心實現(xiàn)方法是調(diào)用rebound方法。
bindif方法核心是調(diào)用bind方法,只不過對容器是否綁定服務(wù)做了一個判斷:
public function bindIf($abstract, $concrete = null, $shared = false) { if (! $this->bound($abstract)) { $this->bind($abstract, $concrete, $shared); } }
singleton是bind方法的一種特例,shared=true表示為單例綁定:
public function singleton($abstract, $concrete = null) { $this->bind($abstract, $concrete, true); }
instance是綁定對象實例到容器中(不用使用make進(jìn)行解析了):
public function instance($abstract, $instance) { $this->removeAbstractAlias($abstract); $isBound = $this->bound($abstract); unset($this->aliases[$abstract]); $this->instances[$abstract] = $instance; if ($isBound) { $this->rebound($abstract); } return $instance; }
數(shù)組綁定是Container類繼承了ArrayAccess接口,在offsetSet中調(diào)用了bind方法進(jìn)行注冊:
public function offsetSet($key, $value) { $this->bind($key, $value instanceof Closure ? $value : function () use ($value) { return $value; }); }
extend方法實現(xiàn)了當(dāng)原來的類注冊或者實例化出來后,對其進(jìn)行拓展:
public function extend($abstract, Closure $closure) { $abstract = $this->getAlias($abstract); if (isset($this->instances[$abstract])) { $this->instances[$abstract] = $closure($this->instances[$abstract], $this); $this->rebound($abstract); } else { $this->extenders[$abstract][] = $closure; if ($this->resolved($abstract)) { $this->rebound($abstract); } } }
Context綁定是針對于兩個類使用同一個接口,但是我們在類中注入了不同的實現(xiàn),這時候我們就需要使用when方法了:
public function when($concrete) { $aliases = []; foreach (Arr::wrap($concrete) as $c) { $aliases[] = $this->getAlias($c); } return new ContextualBindingBuilder($this, $aliases); }
繼續(xù)看ContextualBindingBuilder類的源碼我們知道,上下文綁定的基本思路就是$this->app->when()->needs()->give();
比如有幾個控制器分別依賴IlluminateContractsFilesystemFilesystem的不同實現(xiàn):
$this->app->when(StorageController::class) ->needs(Filesystem::class) ->give(function () { Storage::class });//提供類名 $this->app->when(PhotoController::class) ->needs(Filesystem::class) ->give(function () { return new Storage(); });//提供實現(xiàn)方式 $this->app->when(VideoController::class) ->needs(Filesystem::class) ->give(function () { return new Storage($app->make(Disk::class)); });//需要依賴注入
有一些場景,我們希望當(dāng)接口改變以后對已實例化的對象重新做一些改變,這就是rebinding 函數(shù)的用途:
public function rebinding($abstract, Closure $callback) { $this->reboundCallbacks[$abstract = $this->getAlias($abstract)][] = $callback; if ($this->bound($abstract)) { return $this->make($abstract); } }3.2、服務(wù)別名
在服務(wù)容器解析之前,Lumen框架會將常用的服務(wù)起一些別名,方便系統(tǒng)Facade方法調(diào)用和解析。
public function withAliases($userAliases = []) { $defaults = [ "IlluminateSupportFacadesAuth" => "Auth", "IlluminateSupportFacadesCache" => "Cache", "IlluminateSupportFacadesDB" => "DB", "IlluminateSupportFacadesEvent" => "Event", "IlluminateSupportFacadesGate" => "Gate", "IlluminateSupportFacadesLog" => "Log", "IlluminateSupportFacadesQueue" => "Queue", "IlluminateSupportFacadesRoute" => "Route", "IlluminateSupportFacadesSchema" => "Schema", "IlluminateSupportFacadesStorage" => "Storage", "IlluminateSupportFacadesURL" => "URL", "IlluminateSupportFacadesValidator" => "Validator", ]; if (! static::$aliasesRegistered) { static::$aliasesRegistered = true; $merged = array_merge($defaults, $userAliases); foreach ($merged as $original => $alias) { class_alias($original, $alias); } } } ... protected function registerContainerAliases() { $this->aliases = [ "IlluminateContractsFoundationApplication" => "app", "IlluminateContractsAuthFactory" => "auth", "IlluminateContractsAuthGuard" => "auth.driver", "IlluminateContractsCacheFactory" => "cache", "IlluminateContractsCacheRepository" => "cache.store", "IlluminateContractsConfigRepository" => "config", "IlluminateContainerContainer" => "app", "IlluminateContractsContainerContainer" => "app", "IlluminateDatabaseConnectionResolverInterface" => "db", "IlluminateDatabaseDatabaseManager" => "db", "IlluminateContractsEncryptionEncrypter" => "encrypter", "IlluminateContractsEventsDispatcher" => "events", "IlluminateContractsHashingHasher" => "hash", "log" => "PsrLogLoggerInterface", "IlluminateContractsQueueFactory" => "queue", "IlluminateContractsQueueQueue" => "queue.connection", "request" => "IlluminateHttpRequest", "LaravelLumenRoutingRouter" => "router", "IlluminateContractsTranslationTranslator" => "translator", "LaravelLumenRoutingUrlGenerator" => "url", "IlluminateContractsValidationFactory" => "validator", "IlluminateContractsViewFactory" => "view", ]; } ......
lumen服務(wù)容器中通過alias方法添加服務(wù)別名:
public function alias($abstract, $alias) { $this->aliases[$alias] = $abstract; $this->abstractAliases[$abstract][] = $alias; }
通過getAlias獲得服務(wù)的別名:
public function getAlias($abstract) { if (! isset($this->aliases[$abstract])) { return $abstract; } if ($this->aliases[$abstract] === $abstract) { throw new LogicException("[{$abstract}] is aliased to itself."); } return $this->getAlias($this->aliases[$abstract]);
通過getAlias我們知道,服務(wù)別名是支持遞歸設(shè)置的。
3.3、其他函數(shù)簡述服務(wù)容器解析一個對象時會觸發(fā)resolving和afterResolving函數(shù)。分別在之前之后觸發(fā):
public function resolving($abstract, Closure $callback = null) { if (is_string($abstract)) { $abstract = $this->getAlias($abstract); } if (is_null($callback) && $abstract instanceof Closure) { $this->globalResolvingCallbacks[] = $abstract; } else { $this->resolvingCallbacks[$abstract][] = $callback; } } public function afterResolving($abstract, Closure $callback = null) { if (is_string($abstract)) { $abstract = $this->getAlias($abstract); } if ($abstract instanceof Closure && is_null($callback)) { $this->globalAfterResolvingCallbacks[] = $abstract; } else { $this->afterResolvingCallbacks[$abstract][] = $callback; } }
服務(wù)容器中有一些裝飾函數(shù),wrap裝飾call,factory裝飾make:
public function call($callback, array $parameters = [], $defaultMethod = null) { return BoundMethod::call($this, $callback, $parameters, $defaultMethod); } ...... public function wrap(Closure $callback, array $parameters = []) { return function () use ($callback, $parameters) { return $this->call($callback, $parameters); }; }
服務(wù)容器的解析方法和函數(shù)之前已經(jīng)說過,有幾種常用的方法,這里就不再一一贅述了。
可以服務(wù)容器中flush()方法用于清空容器中所有的服務(wù):
public function flush() { $this->aliases = []; $this->resolved = []; $this->bindings = []; $this->instances = []; $this->abstractAliases = []; }
Lumen中的服務(wù)容器源碼實現(xiàn)非常復(fù)雜,但是對其工作原理了解清楚之后,看起來也就有些頭緒了,每個函數(shù)所做的工作也可以結(jié)合注釋和源碼進(jìn)行理解了。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/31265.html
摘要:的現(xiàn)狀目前是版本,是基于開發(fā)。入口文件啟動文件和配置文件框架的入口文件是。在路由中指定控制器類必須寫全命名空間,不然會提示找不到類。目前支持四種數(shù)據(jù)庫系統(tǒng)以及。使用時發(fā)生錯誤,因為在文件中,的默認(rèn)驅(qū)動是。 最近使用 Lumen 做了 2 個業(yè)余項目,特此記錄和分享一下。 Lumen 的介紹 在使用一項新的技術(shù)時,了解其應(yīng)用場景是首要的事情。 Lumen 的口號:為速度而生的 La...
摘要:如何做用戶認(rèn)證根據(jù)文檔描述,提供用戶認(rèn)證的接口,他的核心是看守器和提供器,看守器定義怎么認(rèn)證用戶,提供器定義怎么檢索用戶。 最近的一個PHP項目,上一個項目是采用ThinkPHP來弄的,因為很早就聽說過Laravel的大名,所以進(jìn)了Laravel的官網(wǎng),意外發(fā)現(xiàn)了Lumen,正好我項目是提供API的,所以選擇了Lumen,因為是Laravel的精簡版,看了幾天的Laravel文檔,也總...
摘要:問題分析通過閱讀源碼發(fā)現(xiàn),中的服務(wù)都是按需綁定并加載。在服務(wù)按需綁定并加載的時候,使用了類似組件的形式通過載入配置項并綁定服務(wù)。因為在這個時候的相關(guān)配置文件還沒有被載入。 問題描述 公司一個高并發(fā)API需要從Laravel移植到Lumen,由于數(shù)據(jù)庫配置信息是通過遠(yuǎn)程或者緩存讀取后動態(tài)配置,所以在中間件時使用到了 Config::set 然而實際運行時發(fā)現(xiàn)數(shù)據(jù)庫配置并沒有更新。 由于是...
摘要:在開發(fā)中,用戶認(rèn)證是核心,是數(shù)據(jù)是否有保障的前提,目前主要有兩種常用方式進(jìn)行用戶認(rèn)證和。附是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于的開放標(biāo)準(zhǔn)。 好久沒寫 PHP 代碼了,尤其是 Lumen,我是 Lumen 的忠實用戶,自從面世開始,我就將 Lumen 作為我 API 的主要框架使用。 但說到 API,不得不說的一個概念:「前后端分離」,現(xiàn)在越來越多的團(tuán)隊都采用前后端分離,徹底解...
摘要:繼續(xù)學(xué)習(xí)分割線看看是怎么輸出這個數(shù)據(jù)目錄下的加載了下的的自動加載加載的配置初始化應(yīng)用初始化的內(nèi)容指定項目基礎(chǔ)目錄注冊服務(wù)容器注冊異常處理實例 繼續(xù)學(xué)習(xí)lumen5.5 -----------------------分割線----------------------- 看看是怎么輸出Lumen (5.5.2) (Laravel Components 5.5.*)這個數(shù)據(jù) public目錄...
閱讀 3893·2021-09-27 13:35
閱讀 1075·2021-09-24 09:48
閱讀 2904·2021-09-22 15:42
閱讀 2345·2021-09-22 15:28
閱讀 3151·2019-08-30 15:43
閱讀 2618·2019-08-30 13:52
閱讀 2976·2019-08-29 12:48
閱讀 1455·2019-08-26 13:55