關(guān)于Laravel服務(wù)容器綁定與解析

下面由laravel教程欄目給大家介紹服務(wù)容器綁定與解析,希望對(duì)需要的朋友有所幫助!

關(guān)于Laravel服務(wù)容器綁定與解析

前言

? 老實(shí)說,第一次老大讓我看laravel框架手冊(cè)的那天早上,我是很絕望的,因?yàn)檎娴臎]接觸過,對(duì)我這種渣渣來(lái)說,laravel的入門門檻確實(shí)有點(diǎn)高了,但還是得硬著頭皮看下去(雖然到現(xiàn)在我還有很多沒看懂,也沒用過)。
? 后面慢慢根據(jù)公司項(xiàng)目的代碼對(duì)laravel也慢慢熟悉起來(lái)了,但還是停留在一些表面的功能,例如依賴注入,ORM操作,用戶認(rèn)證這些和我項(xiàng)目業(yè)務(wù)邏輯相關(guān)的操作,然后對(duì)于一些架構(gòu)基礎(chǔ)的,例如服務(wù)提供器,服務(wù)容器,中間件,Redis等這些一開始就要設(shè)置好的東西,我倒是沒實(shí)際操作過(因?yàn)槔洗笠婚_始就做好了),所以看手冊(cè)還是有點(diǎn)懵。
? 所以有空的時(shí)候逛逛論壇,搜下Google就發(fā)現(xiàn)許多關(guān)于laravel核心架構(gòu)的介紹,以及如何使用的網(wǎng)站(確實(shí)看完后再去看手冊(cè)就好理解多了),下面就根據(jù)一個(gè)我覺得不錯(cuò)的網(wǎng)站上面的教學(xué)來(lái)記錄一下laravel核心架構(gòu)的學(xué)習(xí)
網(wǎng)站地址:https://laraweb.net/ 這是一個(gè)日本的網(wǎng)站,我覺得挺適合新手的,內(nèi)容用瀏覽器翻譯過來(lái)就ok了,畢竟日文直翻過來(lái)很好理解的

關(guān)于服務(wù)容器

? 手冊(cè)上是這樣介紹的:Laravel 服務(wù)容器是用于管理類的依賴和執(zhí)行依賴注入的工具。依賴注入這個(gè)花俏名詞實(shí)質(zhì)上是指:類的依賴項(xiàng)通過構(gòu)造函數(shù),或者某些情況下通過「setter」方法「注入」到類中。。。。。。(真的看不懂啥意思)
? 服務(wù)容器是用于管理類(服務(wù))的實(shí)例化的機(jī)制。直接看看服務(wù)容器怎么用

? 1.在服務(wù)容器中注冊(cè)類(bind)

$this->app->bind('sender','MailSender'); //$this->app成為服務(wù)容器。

? 2.從服務(wù)容器生成類(make)

$sender = $this->app->make('sender'); //從服務(wù)容器($this->app)創(chuàng)建一個(gè)sender類。 在這種情況下,將返回MailSender的實(shí)例。

? 這是服務(wù)容器最簡(jiǎn)單的使用,下面是對(duì)服務(wù)容器的詳細(xì)介紹
(主要參考:https://www.cnblogs.com/lyzg/…)

laravel容器基本認(rèn)識(shí)

? 一開始,index.php 文件加載 Composer 生成定義的自動(dòng)加載器,然后從 bootstrap/app.php 腳本中檢索 Laravel 應(yīng)用程序的實(shí)例。Laravel 本身采取的第一個(gè)動(dòng)作是創(chuàng)建一個(gè) application/ service container 的實(shí)例。

$app = new IlluminateFoundationApplication(     dirname(__DIR__) );

? 這個(gè)文件在每一次請(qǐng)求到達(dá)laravel框架都會(huì)執(zhí)行,所創(chuàng)建的$app即是laravel框架的應(yīng)用程序?qū)嵗谡麄€(gè)請(qǐng)求生命周期都是唯一的。laravel提供了很多服務(wù),包括認(rèn)證,數(shù)據(jù)庫(kù),緩存,消息隊(duì)列等等,$app作為一個(gè)容器管理工具,負(fù)責(zé)幾乎所有服務(wù)組件的實(shí)例化以及實(shí)例的生命周期管理。當(dāng)需要一個(gè)服務(wù)類來(lái)完成某個(gè)功能的時(shí)候,僅需要通過容器解析出該類型的一個(gè)實(shí)例即可。從最終的使用方式來(lái)看,laravel容器對(duì)服務(wù)實(shí)例的管理主要包括以下幾個(gè)方面:

  • 服務(wù)的綁定與解析
  • 服務(wù)提供者的管理
  • 別名的作用
  • 依賴注入

先了解如何在代碼中獲取到容器實(shí)例,再學(xué)習(xí)上面四個(gè)關(guān)鍵

如何在代碼中獲取到容器實(shí)例

第一種是

$app = app(); //app這個(gè)輔助函數(shù)定義在vendorlaravelframeworksrcIlluminateFoundationhelper.php 里面,,這個(gè)文件定義了很多help函數(shù),并且會(huì)通過composer自動(dòng)加載到項(xiàng)目中。 所以,在參與http請(qǐng)求處理的任何代碼位置都能夠訪問其中的函數(shù),比如app()。

第二種是

Route::get('/', function () {     dd(App::basePath());     return ''; }); //這個(gè)其實(shí)是用到Facade,中文直譯貌似叫門面,在config/app.php中, 有一節(jié)數(shù)組aliases專門用來(lái)配置一些類型的別名,第一個(gè)就是'App' => IlluminateSupportFacadesApp::class, 具體的Google一下laravel有關(guān)門面的具體實(shí)現(xiàn)方式

第三種是

? 在服務(wù)提供者里面直接使用$this->app。服務(wù)提供者后面還會(huì)介紹,現(xiàn)在只是引入。因?yàn)榉?wù)提供者類都是由laravel容器實(shí)例化的,這些類都繼承自IlluminateSupportServiceProvider,它定義了一個(gè)實(shí)例屬性$app:

abstract class ServiceProvider {     protected $app;

? laravel在實(shí)例化服務(wù)提供者的時(shí)候,會(huì)把laravel容器實(shí)例注入到這個(gè)$app上面。所以我們?cè)诜?wù)提供者里面,始終能通過$this->$app訪問到laravel容器實(shí)例,而不需要再使用app()函數(shù)或者App Facade了。

如何理解服務(wù)綁定與解析

? 淺義層面理解,容器既然用來(lái)存儲(chǔ)對(duì)象,那么就要有一個(gè)對(duì)象存入跟對(duì)象取出的過程。這個(gè)對(duì)象存入跟對(duì)象取出的過程在laravel里面稱為服務(wù)的綁定與解析。

app()->bind('service', 'this is service1');  app()->bind('service2', [     'hi' => function(){         //say hi     } ]);  class Service {  }  app()->bind('service3', function(){     return new Service(); });

? 還有一個(gè)單例綁定singleton,是bind的一種特殊情況(第三個(gè)參數(shù)為true),綁定到容器的對(duì)象只會(huì)被解析一次,之后的調(diào)用都返回相同的實(shí)例

public function singleton($abstract, $concrete = null) { $this->bind($abstract, $concrete, true); }

? 在綁定的時(shí)候,我們可以直接綁定已經(jīng)初始化好的數(shù)據(jù)(基本類型、數(shù)組、對(duì)象實(shí)例),還可以用匿名函數(shù)來(lái)綁定。用匿名函數(shù)的好處在于,這個(gè)服務(wù)綁定到容器以后,并不會(huì)立即產(chǎn)生服務(wù)最終的對(duì)象,只有在這個(gè)服務(wù)解析的時(shí)候,匿名函數(shù)才會(huì)執(zhí)行,此時(shí)才會(huì)產(chǎn)生這個(gè)服務(wù)對(duì)應(yīng)的服務(wù)實(shí)例。
? 實(shí)際上,當(dāng)我們使用singleton,bind方法以及數(shù)組形式,(這三個(gè)方法是后面要介紹的綁定的方法),進(jìn)行服務(wù)綁定的時(shí)候,如果綁定的服務(wù)形式,不是一個(gè)匿名函數(shù),也會(huì)在laravel內(nèi)部用一個(gè)匿名函數(shù)包裝起來(lái),這樣的話, 不輪綁定什么內(nèi)容,都能做到前面介紹的懶初始化的功能,這對(duì)于容器的性能是有好處的。這個(gè)可以從bind的源碼中看到一些細(xì)節(jié):

if (! $concrete instanceof Closure) {     $concrete = $this->getClosure($abstract, $concrete); }

看看bind的底層代碼

public function bind($abstract, $concrete = null, $shared = false)

? 第一個(gè)參數(shù)服務(wù)綁定名稱,第二個(gè)參數(shù)服務(wù)綁定的結(jié)果(也就是閉包,得到實(shí)例),第三個(gè)參數(shù)就表示這個(gè)服務(wù)是否在多次解析的時(shí)候,始終返回第一次解析出的實(shí)例(也就是單例綁定singleton)。

? 服務(wù)綁定還可以通過數(shù)組的方式:

app()['service'] = function(){     return new Service(); };

綁定大概就這些,接下來(lái)看解析,也就是取出來(lái)用

$service= app()->make('service');

? 這個(gè)方法接收兩個(gè)參數(shù),第一個(gè)是服務(wù)的綁定名稱和服務(wù)綁定名稱的別名,如果是別名,那么就會(huì)根據(jù)服務(wù)綁定名稱的別名配置,找到最終的服務(wù)綁定名稱,然后進(jìn)行解析;第二個(gè)參數(shù)是一個(gè)數(shù)組,最終會(huì)傳遞給服務(wù)綁定產(chǎn)生的閉包。

看源碼:

/**  * Resolve the given type from the container.  *  * @param  string  $abstract  * @param  array  $parameters  * @return mixed  */ public function make($abstract, array $parameters = []) {     return $this->resolve($abstract, $parameters); }  /**  * Resolve the given type from the container.  *  * @param  string  $abstract  * @param  array  $parameters  * @return mixed  */ protected function resolve($abstract, $parameters = []) {     $abstract = $this->getAlias($abstract);      $needsContextualBuild = ! empty($parameters) || ! is_null(         $this->getContextualConcrete($abstract)     );      // If an instance of the type is currently being managed as a singleton we'll     // just return an existing instance instead of instantiating new instances     // so the developer can keep using the same objects instance every time.     if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {         return $this->instances[$abstract];     }      $this->with[] = $parameters;      $concrete = $this->getConcrete($abstract);      // We're ready to instantiate an instance of the concrete type registered for     // the binding. This will instantiate the types, as well as resolve any of     // its "nested" dependencies recursively until all have gotten resolved.     if ($this->isBuildable($concrete, $abstract)) {         $object = $this->build($concrete);     } else {         $object = $this->make($concrete);     }      // If we defined any extenders for this type, we'll need to spin through them     // and apply them to the object being built. This allows for the extension     // of services, such as changing configuration or decorating the object.     foreach ($this->getExtenders($abstract) as $extender) {         $object = $extender($object, $this);     }      // If the requested type is registered as a singleton we'll want to cache off     // the instances in "memory" so we can return it later without creating an     // entirely new instance of an object on each subsequent request for it.     if ($this->isShared($abstract) && ! $needsContextualBuild) {         $this->instances[$abstract] = $object;     }      $this->fireResolvingCallbacks($abstract, $object);      // Before returning, we will also set the resolved flag to "true" and pop off     // the parameter overrides for this build. After those two things are done     // we will be ready to return back the fully constructed class instance.     $this->resolved[$abstract] = true;      array_pop($this->with);      return $object; }

第一步:

$needsContextualBuild = ! empty($parameters) || ! is_null(     $this->getContextualConcrete($abstract) );

? 該方法主要是區(qū)分,解析的對(duì)象是否有參數(shù),如果有參數(shù),還需要對(duì)參數(shù)做進(jìn)一步的分析,因?yàn)閭魅氲膮?shù),也可能是依賴注入的,所以還需要對(duì)傳入的參數(shù)進(jìn)行解析;這個(gè)后面再分析。

第二步:

if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {     return $this->instances[$abstract]; }

? 如果是綁定的單例,并且不需要上面的參數(shù)依賴。我們就可以直接返回 $this->instances[$abstract]。

第三步:

$concrete = $this->getConcrete($abstract);  ...  /**  * Get the concrete type for a given abstract.  *  * @param  string  $abstract  * @return mixed   $concrete  */ protected function getConcrete($abstract) {     if (! is_null($concrete = $this->getContextualConcrete($abstract))) {         return $concrete;     }      // If we don't have a registered resolver or concrete for the type, we'll just     // assume each type is a concrete name and will attempt to resolve it as is     // since the container should be able to resolve concretes automatically.     if (isset($this->bindings[$abstract])) {         return $this->bindings[$abstract]['concrete'];     }      return $abstract; }

? 這一步主要是先從綁定的上下文找,是不是可以找到綁定類;如果沒有,則再?gòu)?$bindings[] 中找關(guān)聯(lián)的實(shí)現(xiàn)類;最后還沒有找到的話,就直接返回 $abstract 本身。

// We're ready to instantiate an instance of the concrete type registered for // the binding. This will instantiate the types, as well as resolve any of // its "nested" dependencies recursively until all have gotten resolved. if ($this->isBuildable($concrete, $abstract)) {     $object = $this->build($concrete); } else {     $object = $this->make($concrete); }  ...  /**  * Determine if the given concrete is buildable.  *  * @param  mixed   $concrete  * @param  string  $abstract  * @return bool  */ protected function isBuildable($concrete, $abstract) {     return $concrete === $abstract || $concrete instanceof Closure; }

? 如果之前找到的 $concrete 返回的是 $abstract 值,或者 $concrete 是個(gè)閉包,則執(zhí)行 $this->build($concrete),否則,表示存在嵌套依賴的情況,則采用遞歸的方法執(zhí)行 $this->make($concrete),直到所有的都解析完為止。

$this->build($concrete)

/**  * Instantiate a concrete instance of the given type.  *  * @param  string  $concrete  * @return mixed  *  * @throws IlluminateContractsContainerBindingResolutionException  */ public function build($concrete) {     // If the concrete type is actually a Closure, we will just execute it and     // hand back the results of the functions, which allows functions to be     // used as resolvers for more fine-tuned resolution of these objects.     // 如果傳入的是閉包,則直接執(zhí)行閉包函數(shù),返回結(jié)果     if ($concrete instanceof Closure) {         return $concrete($this, $this->getLastParameterOverride());     }      // 利用反射機(jī)制,解析該類。     $reflector = new ReflectionClass($concrete);      // If the type is not instantiable, the developer is attempting to resolve     // an abstract type such as an Interface of Abstract Class and there is     // no binding registered for the abstractions so we need to bail out.     if (! $reflector->isInstantiable()) {         return $this->notInstantiable($concrete);     }      $this->buildStack[] = $concrete;      // 獲取構(gòu)造函數(shù)     $constructor = $reflector->getConstructor();      // If there are no constructors, that means there are no dependencies then     // we can just resolve the instances of the objects right away, without     // resolving any other types or dependencies out of these containers.     // 如果沒有構(gòu)造函數(shù),則表明沒有傳入?yún)?shù),也就意味著不需要做對(duì)應(yīng)的上下文依賴解析。     if (is_null($constructor)) {         // 將 build 過程的內(nèi)容 pop,然后直接構(gòu)造對(duì)象輸出。         array_pop($this->buildStack);          return new $concrete;     }      // 獲取構(gòu)造函數(shù)的參數(shù)     $dependencies = $constructor->getParameters();      // Once we have all the constructor's parameters we can create each of the     // dependency instances and then use the reflection instances to make a     // new instance of this class, injecting the created dependencies in.     // 解析出所有上下文依賴對(duì)象,帶入函數(shù),構(gòu)造對(duì)象輸出     $instances = $this->resolveDependencies(         $dependencies     );      array_pop($this->buildStack);      return $reflector->newInstanceArgs($instances); }

上面這一段有關(guān)解析make的介紹主要參考:
coding01:看 Laravel 源代碼了解 Container(https://juejin.cn/post/6844903608052367368)

? 這一篇就主要學(xué)習(xí)laravel的服務(wù)容器以及它的綁定和解析,雖然目前能力無(wú)法對(duì)框架源碼每一個(gè)地方都弄懂,但通過這幾篇優(yōu)秀的文章,我將其進(jìn)行整理結(jié)合,這過程讓我更加理解laravel的一些核心內(nèi)容,起碼別人問起來(lái)我多多少少能說出一些,這就是進(jìn)步。

? 后面有關(guān)服務(wù)提供者,依賴注入,中間件等內(nèi)容的學(xué)習(xí)將放在后續(xù)的博客文章中,歡迎看看我的其他博客文章:https://zgxxx.github.io/。
? 以上相關(guān)知識(shí)的引用已經(jīng)注明出處,若有侵權(quán),請(qǐng)聯(lián)系我,感謝這些優(yōu)秀文章的作者

相關(guān)推薦:最新的五個(gè)Laravel視頻教程

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊7 分享