国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > PHP > 正文

Laravel服務(wù)容器(IocContainer)的解讀

2020-03-22 17:48:16
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
這篇文章主要介紹了關(guān)于Laravel服務(wù)容器(IocContainer)的解讀,有著一定的參考價(jià)值,現(xiàn)在分享給大家,有需要的朋友可以參考一下

Laravel的核心是IocContainer, 文檔中稱其為“服務(wù)容器”,服務(wù)容器是一個(gè)用于管理類依賴和執(zhí)行依賴注入的強(qiáng)大工具,Laravel中的功能模塊比如 Route、Eloquent ORM、Request、Response等等等等,實(shí)際上都是與核心無(wú)關(guān)的類模塊提供的,這些類從注冊(cè)到實(shí)例化,最終被我們所使用,其實(shí)都是 laravel 的服務(wù)容器負(fù)責(zé)的。

如果對(duì)服務(wù)容器是什么沒(méi)有清晰概念的話推薦一篇博文來(lái)了解一下服務(wù)容器的來(lái)龍去脈:laravel神奇的服務(wù)容器

服務(wù)容器中有兩個(gè)概念控制反轉(zhuǎn)(IOC)和依賴注入(DI):

依賴注入和控制反轉(zhuǎn)是對(duì)同一件事情的不同描述,它們描述的角度不同。依賴注入是從html' target='_blank'>應(yīng)用程序的角度在描述,應(yīng)用程序依賴容器創(chuàng)建并注入它所需要的外部資源。而控制反轉(zhuǎn)是從容器的角度在描述,容器控制應(yīng)用程序,由容器反向的向應(yīng)用程序注入應(yīng)用程序所需要的外部資源。

在Laravel中框架把自帶的各種服務(wù)綁定到服務(wù)容器,我們也可以綁定自定義服務(wù)到容器。當(dāng)應(yīng)用程序需要使用某一個(gè)服務(wù)時(shí),服務(wù)容器會(huì)講服務(wù)解析出來(lái)同時(shí)自動(dòng)解決服務(wù)之間的依賴然后交給應(yīng)用程序使用。

本篇就來(lái)探討一下Laravel中的服務(wù)綁定和解析是如何實(shí)現(xiàn)的

服務(wù)綁定

常用的綁定服務(wù)到容器的方法有instance, bind, singleton, alias。下面我們分別來(lái)看一下。

instance

將一個(gè)已存在的對(duì)象綁定到服務(wù)容器里,隨后通過(guò)名稱解析該服務(wù)時(shí),容器將總返回這個(gè)綁定的實(shí)例。

$api = new HelpSpot/API(new HttpClient);$this- app- instance( HelpSpot/Api , $api);

會(huì)把對(duì)象注冊(cè)到服務(wù)容器的$instnces屬性里

[ HelpSpot/Api = $api//$api是API類的對(duì)象,這里簡(jiǎn)寫(xiě)了 ]
bind

綁定服務(wù)到服務(wù)容器

有三種綁定方式:

1.綁定自身$this- app- bind( HelpSpot/API , null);2.綁定閉包$this- app- bind( HelpSpot/API , function () { return new HelpSpot/API();});//閉包直接提供類實(shí)現(xiàn)方式$this- app- bind( HelpSpot/API , function ($app) { return new HelpSpot/API($app- make( HttpClient });//閉包返回需要依賴注入的類3. 綁定接口和實(shí)現(xiàn)$this- app- bind( Illuminate/Tests/Container/IContainerContractStub , Illuminate/Tests/Container/ContainerImplementationStub 

針對(duì)第一種情況,其實(shí)在bind方法內(nèi)部會(huì)在綁定服務(wù)之前通過(guò)getClosure()為服務(wù)生成閉包,我們來(lái)看一下bind方法源碼。

public function bind($abstract, $concrete = null, $shared = false) $abstract = $this- normalize($abstract); $concrete = $this- normalize($concrete); //如果$abstract為數(shù)組類似[ Illuminate/ServiceName = service_alias ] //抽取別名 service_alias 并且注冊(cè)到$aliases[]中 //注意:數(shù)組綁定別名的方式在5.4中被移除,別名綁定請(qǐng)使用下面的alias方法 if (is_array($abstract)) { list($abstract, $alias) = $this- extractAlias($abstract); $this- alias($abstract, $alias); $this- dropStaleInstances($abstract); if (is_null($concrete)) { $concrete = $abstract; //如果只提供$abstract,則在這里為其生成concrete閉包 if (! $concrete instanceof Closure) { $concrete = $this- getClosure($abstract, $concrete); $this- bindings[$abstract] = compact( concrete , shared  if ($this- resolved($abstract)) { $this- rebound($abstract);
// $c 就是$container,即服務(wù)容器,會(huì)在回調(diào)時(shí)傳遞給這個(gè)變量 return function ($c, $parameters = []) use ($abstract, $concrete) { $method = ($abstract == $concrete) ? build : make return $c- $method($concrete, $parameters);}

bind把服務(wù)注冊(cè)到服務(wù)容器的$bindings屬性里類似這樣:

$bindings = [ HelpSpot/API = [//閉包綁定 concrete = function ($app, $paramters = []) { return $app- build( HelpSpot/API  shared = false//如果是singleton綁定,這個(gè)值為true Illuminate/Tests/Container/IContainerContractStub = [//接口實(shí)現(xiàn)綁定 concrete = Illuminate/Tests/Container/ContainerImplementationStub , shared = false]
singleton
public function singleton($abstract, $concrete = null) $this- bind($abstract, $concrete, true);}

singleton 方法是bind方法的變種,綁定一個(gè)只需要解析一次的類或接口到容器,然后接下來(lái)對(duì)于容器的調(diào)用該服務(wù)將會(huì)返回同一個(gè)實(shí)例

alias

把服務(wù)和服務(wù)別名注冊(cè)到容器:

public function alias($abstract, $alias) $this- aliases[$alias] = $this- normalize($abstract);}

alias 方法在上面講bind方法里有用到過(guò),它會(huì)把把服務(wù)別名和服務(wù)類的對(duì)應(yīng)關(guān)系注冊(cè)到服務(wù)容器的$aliases屬性里。
例如:

$this- app- alias( /Illuminate/ServiceName , service_alias 

綁定完服務(wù)后在使用時(shí)就可以通過(guò)

$this- app- make( service_alias 

將服務(wù)對(duì)象解析出來(lái),這樣make的時(shí)候就不用寫(xiě)那些比較長(zhǎng)的類名稱了,對(duì)make方法的使用體驗(yàn)上有很大提升。

服務(wù)解析

make: 從服務(wù)容器中解析出服務(wù)對(duì)象,該方法接收你想要解析的類名或接口名作為參數(shù)

/** * Resolve the given type from the container. * @param string $abstract * @param array $parameters * @return mixedpublic function make($abstract, array $parameters = []) //getAlias方法會(huì)假定$abstract是綁定的別名,從$aliases找到映射的真實(shí)類型名 //如果沒(méi)有映射則$abstract即為真實(shí)類型名,將$abstract原樣返回 $abstract = $this- getAlias($this- normalize($abstract)); // 如果服務(wù)是通過(guò)instance()方式綁定的,就直接解析返回綁定的service if (isset($this- instances[$abstract])) { return $this- instances[$abstract]; // 獲取$abstract接口對(duì)應(yīng)的$concrete(接口的實(shí)現(xiàn)) $concrete = $this- getConcrete($abstract); if ($this- isBuildable($concrete, $abstract)) { $object = $this- build($concrete, $parameters); } else { //如果時(shí)接口實(shí)現(xiàn)這種綁定方式,通過(guò)接口拿到實(shí)現(xiàn)后需要再make一次才能 //滿足isBuildable的條件 ($abstract === $concrete) $object = $this- make($concrete, $parameters); foreach ($this- getExtenders($abstract) as $extender) { $object = $extender($object, $this); //如果服務(wù)是以singleton方式注冊(cè)進(jìn)來(lái)的則,把構(gòu)建好的服務(wù)對(duì)象放到$instances里, //避免下次使用時(shí)重新構(gòu)建 if ($this- isShared($abstract)) { $this- instances[$abstract] = $object; $this- fireResolvingCallbacks($abstract, $object); $this- resolved[$abstract] = true; return $object;protected function getConcrete($abstract) if (! is_null($concrete = $this- getContextualConcrete($abstract))) { return $concrete; // 如果是$abstract之前沒(méi)有注冊(cè)類實(shí)現(xiàn)到服務(wù)容器里,則服務(wù)容器會(huì)認(rèn)為$abstract本身就是接口的類實(shí)現(xiàn) if (! isset($this- bindings[$abstract])) { return $abstract; return $this- bindings[$abstract][ concrete protected function isBuildable($concrete, $abstract) return $concrete === $abstract || $concrete instanceof Closure;}

通過(guò)對(duì)make方法的梳理我們發(fā)現(xiàn),build方法的職能是構(gòu)建解析出來(lái)的服務(wù)的對(duì)象的,下面看一下構(gòu)建對(duì)象的具體流程。(構(gòu)建過(guò)程中用到了PHP類的反射來(lái)實(shí)現(xiàn)服務(wù)的依賴注入)

public function build($concrete, array $parameters = []) // 如果是閉包直接執(zhí)行閉包并返回(對(duì)應(yīng)閉包綁定) if ($concrete instanceof Closure) { return $concrete($this, $parameters); // 使用反射ReflectionClass來(lái)對(duì)實(shí)現(xiàn)類進(jìn)行反向工程 $reflector = new ReflectionClass($concrete); // 如果不能實(shí)例化,這應(yīng)該是接口或抽象類,再或者就是構(gòu)造函數(shù)是private的 if (! $reflector- isInstantiable()) { if (! empty($this- buildStack)) { $previous = implode( , , $this- buildStack); $message = Target [$concrete] is not instantiable while building [$previous].  } else { $message = Target [$concrete] is not instantiable.  throw new BindingResolutionException($message); $this- buildStack[] = $concrete; // 獲取構(gòu)造函數(shù) $constructor = $reflector- getConstructor(); // 如果構(gòu)造函數(shù)是空,說(shuō)明沒(méi)有任何依賴,直接new返回 if (is_null($constructor)) { array_pop($this- buildStack); return new $concrete; // 獲取構(gòu)造函數(shù)的依賴(形參),返回一組ReflectionParameter對(duì)象組成的數(shù)組表示每一個(gè)參數(shù) $dependencies = $constructor- getParameters(); $parameters = $this- keyParametersByArgument( $dependencies, $parameters // 構(gòu)建構(gòu)造函數(shù)需要的依賴 $instances = $this- getDependencies( $dependencies, $parameters array_pop($this- buildStack); return $reflector- newInstanceArgs($instances);//獲取依賴protected function getDependencies(array $parameters, array $primitives = []) $dependencies = []; foreach ($parameters as $parameter) { $dependency = $parameter- getClass(); // 某一依賴值在$primitives中(即build方法的$parameters參數(shù))已提供 // $parameter- name返回參數(shù)名 if (array_key_exists($parameter- name, $primitives)) { $dependencies[] = $primitives[$parameter- name]; elseif (is_null($dependency)) { // 參數(shù)的ReflectionClass為null,說(shuō)明是基本類型,如 int , string  $dependencies[] = $this- resolveNonClass($parameter); } else { // 參數(shù)是一個(gè)類的對(duì)象, 則用resolveClass去把對(duì)象解析出來(lái) $dependencies[] = $this- resolveClass($parameter); return $dependencies;//解析出依賴類的對(duì)象protected function resolveClass(ReflectionParameter $parameter) try { // $parameter- getClass()- name返回的是類名(參數(shù)在typehint里聲明的類型) // 然后遞歸繼續(xù)make(在make時(shí)發(fā)現(xiàn)依賴類還有其他依賴,那么會(huì)繼續(xù)make依賴的依賴 // 直到所有依賴都被解決了build才結(jié)束) return $this- make($parameter- getClass()- name); } catch (BindingResolutionException $e) { if ($parameter- isOptional()) { return $parameter- getDefaultValue(); throw $e;}

服務(wù)容器就是laravel的核心, 它通過(guò)依賴注入很好的替我們解決對(duì)象之間的相互依賴關(guān)系,而又通過(guò)控制反轉(zhuǎn)讓外部來(lái)來(lái)定義具體的行為(Route, Eloquent這些都是外部模塊,它們自己定義了行為規(guī)范,這些類從注冊(cè)到實(shí)例化給你使用才是服務(wù)容器負(fù)責(zé)的)。

一個(gè)類要被容器所能夠提取,必須要先注冊(cè)至這個(gè)容器。既然 laravel 稱這個(gè)容器叫做服務(wù)容器,那么我們需要某個(gè)服務(wù),就得先注冊(cè)、綁定這個(gè)服務(wù)到容器,那么提供服務(wù)并綁定服務(wù)至容器的東西,就是服務(wù)提供器(ServiceProvider)。服務(wù)提供者主要分為兩個(gè)部分,register(注冊(cè)) 和 boot(引導(dǎo)、初始化)由于篇幅問(wèn)題關(guān)于Laravel服務(wù)提供器的內(nèi)容請(qǐng)看另一篇Laravel核心解讀--服務(wù)提供器(ServiceProvider)。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,更多相關(guān)內(nèi)容請(qǐng)關(guān)注PHP !

相關(guān)推薦:

Laravel核心解讀Request

Laravel服務(wù)提供器(ServiceProvider)的解讀

以上就是Laravel服務(wù)容器(IocContainer)的解讀的詳細(xì)內(nèi)容,PHP教程

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 托克托县| 滁州市| 富川| 桐城市| 商河县| 卓资县| 晋城| 灵川县| 黔西| 铅山县| 宣化县| 沁水县| 石河子市| 凌海市| 焉耆| 安新县| 专栏| 丹寨县| 佛冈县| 界首市| 榆社县| 郁南县| 普格县| 庆阳市| 田阳县| 泸西县| 漾濞| 平凉市| 湾仔区| 磐安县| 日照市| 通榆县| 固镇县| 读书| 浦北县| 龙泉市| 泉州市| 蓬溪县| 柳林县| 玉山县| 长春市|