摘要:用匿名函數的好處在于,這個服務綁定到容器以后,并不會立即產生服務最終的對象,只有在這個服務解析的時候,匿名函數才會執行,此時才會產生這個服務對應的服務實例。
前言
? 老實說,第一次老大讓我看laravel框架手冊的那天早上,我是很絕望的,因為真的沒接觸過,對我這種渣渣來說,laravel的入門門檻確實有點高了,但還是得硬著頭皮看下去(雖然到現在我還有很多沒看懂,也沒用過)。
? 后面慢慢根據公司項目的代碼對laravel也慢慢熟悉起來了,但還是停留在一些表面的功能,例如依賴注入,ORM操作,用戶認證這些和我項目業務邏輯相關的操作,然后對于一些架構基礎的,例如服務提供器,服務容器,中間件,Redis等這些一開始就要設置好的東西,我倒是沒實際操作過(因為老大一開始就做好了),所以看手冊還是有點懵。
? 所以有空的時候逛逛論壇,搜下Google就發現許多關于laravel核心架構的介紹,以及如何使用的網站(確實看完后再去看手冊就好理解多了),下面就根據一個我覺得不錯的網站上面的教學來記錄一下laravel核心架構的學習
網站地址:https://laraweb.net/ 這是一個日本的網站,我覺得挺適合新手的,內容用瀏覽器翻譯過來就ok了,畢竟日文直翻過來很好理解的
? 手冊上是這樣介紹的:Laravel 服務容器是用于管理類的依賴和執行依賴注入的工具。依賴注入這個花俏名詞實質上是指:類的依賴項通過構造函數,或者某些情況下通過「setter」方法「注入」到類中。。。。。。(真的看不懂啥意思)
? 服務容器是用于管理類(服務)的實例化的機制。直接看看服務容器怎么用
? 1.在服務容器中注冊類(bind)
$this->app->bind("sender","MailSender"); //$this->app成為服務容器。
? 2.從服務容器生成類(make)
$sender = $this->app->make("sender"); //從服務容器($this->app)創建一個sender類。 在這種情況下,將返回MailSender的實例。
? 這是服務容器最簡單的使用,下面是對服務容器的詳細介紹
(主要參考:https://www.cnblogs.com/lyzg/...)
? 一開始,index.php 文件加載 Composer 生成定義的自動加載器,然后從 bootstrap/app.php 腳本中檢索 Laravel 應用程序的實例。Laravel 本身采取的第一個動作是創建一個 application/ service container 的實例。
$app = new IlluminateFoundationApplication( dirname(__DIR__) );
? 這個文件在每一次請求到達laravel框架都會執行,所創建的$app即是laravel框架的應用程序實例,它在整個請求生命周期都是唯一的。laravel提供了很多服務,包括認證,數據庫,緩存,消息隊列等等,$app作為一個容器管理工具,負責幾乎所有服務組件的實例化以及實例的生命周期管理。當需要一個服務類來完成某個功能的時候,僅需要通過容器解析出該類型的一個實例即可。從最終的使用方式來看,laravel容器對服務實例的管理主要包括以下幾個方面:
服務的綁定與解析
服務提供者的管理
別名的作用
依賴注入
先了解如何在代碼中獲取到容器實例,再學習上面四個關鍵
如何在代碼中獲取到容器實例第一種是
$app = app(); //app這個輔助函數定義在vendorlaravelframeworksrcIlluminateFoundationhelper.php 里面,,這個文件定義了很多help函數,并且會通過composer自動加載到項目中。 所以,在參與http請求處理的任何代碼位置都能夠訪問其中的函數,比如app()。
第二種是
Route::get("/", function () { dd(App::basePath()); return ""; }); //這個其實是用到Facade,中文直譯貌似叫門面,在config/app.php中, 有一節數組aliases專門用來配置一些類型的別名,第一個就是"App" => IlluminateSupportFacadesApp::class, 具體的Google一下laravel有關門面的具體實現方式
第三種是
? 在服務提供者里面直接使用$this->app。服務提供者后面還會介紹,現在只是引入。因為服務提供者類都是由laravel容器實例化的,這些類都繼承自IlluminateSupportServiceProvider,它定義了一個實例屬性$app:
abstract class ServiceProvider { protected $app;
? laravel在實例化服務提供者的時候,會把laravel容器實例注入到這個$app上面。所以我們在服務提供者里面,始終能通過$this->$app訪問到laravel容器實例,而不需要再使用app()函數或者App Facade了。
如何理解服務綁定與解析? 淺義層面理解,容器既然用來存儲對象,那么就要有一個對象存入跟對象取出的過程。這個對象存入跟對象取出的過程在laravel里面稱為服務的綁定與解析。
app()->bind("service", "this is service1"); app()->bind("service2", [ "hi" => function(){ //say hi } ]); class Service { } app()->bind("service3", function(){ return new Service(); });
? 還有一個單例綁定singleton,是bind的一種特殊情況(第三個參數為true),綁定到容器的對象只會被解析一次,之后的調用都返回相同的實例
public function singleton($abstract, $concrete = null) { $this->bind($abstract, $concrete, true); }
? 在綁定的時候,我們可以直接綁定已經初始化好的數據(基本類型、數組、對象實例),還可以用匿名函數來綁定。用匿名函數的好處在于,這個服務綁定到容器以后,并不會立即產生服務最終的對象,只有在這個服務解析的時候,匿名函數才會執行,此時才會產生這個服務對應的服務實例。
? 實際上,當我們使用singleton,bind方法以及數組形式,(這三個方法是后面要介紹的綁定的方法),進行服務綁定的時候,如果綁定的服務形式,不是一個匿名函數,也會在laravel內部用一個匿名函數包裝起來,這樣的話, 不輪綁定什么內容,都能做到前面介紹的懶初始化的功能,這對于容器的性能是有好處的。這個可以從bind的源碼中看到一些細節:
if (! $concrete instanceof Closure) { $concrete = $this->getClosure($abstract, $concrete); }
看看bind的底層代碼
public function bind($abstract, $concrete = null, $shared = false)
? 第一個參數服務綁定名稱,第二個參數服務綁定的結果(也就是閉包,得到實例),第三個參數就表示這個服務是否在多次解析的時候,始終返回第一次解析出的實例(也就是單例綁定singleton)。
? 服務綁定還可以通過數組的方式:
app()["service"] = function(){ return new Service(); };
綁定大概就這些,接下來看解析,也就是取出來用
$service= app()->make("service");
? 這個方法接收兩個參數,第一個是服務的綁定名稱和服務綁定名稱的別名,如果是別名,那么就會根據服務綁定名稱的別名配置,找到最終的服務綁定名稱,然后進行解析;第二個參數是一個數組,最終會傳遞給服務綁定產生的閉包。
看源碼:
/** * 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) );
? 該方法主要是區分,解析的對象是否有參數,如果有參數,還需要對參數做進一步的分析,因為傳入的參數,也可能是依賴注入的,所以還需要對傳入的參數進行解析;這個后面再分析。
第二步:
if (isset($this->instances[$abstract]) && ! $needsContextualBuild) { return $this->instances[$abstract]; }
? 如果是綁定的單例,并且不需要上面的參數依賴。我們就可以直接返回 $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; }
? 這一步主要是先從綁定的上下文找,是不是可以找到綁定類;如果沒有,則再從 $bindings[] 中找關聯的實現類;最后還沒有找到的話,就直接返回 $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 是個閉包,則執行 $this->build($concrete),否則,表示存在嵌套依賴的情況,則采用遞歸的方法執行 $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. // 如果傳入的是閉包,則直接執行閉包函數,返回結果 if ($concrete instanceof Closure) { return $concrete($this, $this->getLastParameterOverride()); } // 利用反射機制,解析該類。 $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; // 獲取構造函數 $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. // 如果沒有構造函數,則表明沒有傳入參數,也就意味著不需要做對應的上下文依賴解析。 if (is_null($constructor)) { // 將 build 過程的內容 pop,然后直接構造對象輸出。 array_pop($this->buildStack); return new $concrete; } // 獲取構造函數的參數 $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. // 解析出所有上下文依賴對象,帶入函數,構造對象輸出 $instances = $this->resolveDependencies( $dependencies ); array_pop($this->buildStack); return $reflector->newInstanceArgs($instances); }
上面這一段有關解析make的介紹主要參考:
coding01:看 Laravel 源代碼了解 Container
? 這一篇就主要學習laravel的服務容器以及它的綁定和解析,雖然目前能力無法對框架源碼每一個地方都弄懂,但通過這幾篇優秀的文章,我將其進行整理結合,這過程讓我更加理解laravel的一些核心內容,起碼別人問起來我多多少少能說出一些,這就是進步。
? 后面有關服務提供者,依賴注入,中間件等內容的學習將放在后續的博客文章中,歡迎看看我的其他博客文章:https://zgxxx.github.io/。
? 以上相關知識的引用已經注明出處,若有侵權,請聯系我,感謝這些優秀文章的作者
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/29524.html
摘要:有一種機制來定義和執行每個服務的初始處理,實現初始處理的類稱為服務提供者。在里面,要自定一個服務提供者非常容易,只要繼承這個類即可。這個注冊的過程其實就是前面說的實例化服務提供者的類,并調用方法的過程。 前言 ? 上一篇博客文章收集了關于Laravel服務容器的相關知識(傳送門),我們知道了服務容器主要有綁定和解析兩個重要功能,那么Laravel這個框架集齊了如此多功能,我們項目可能還...
摘要:劃下重點,服務容器是用于管理類的依賴和執行依賴注入的工具。類的實例化及其依賴的注入,完全由服務容器自動的去完成。 本文首發于 深入剖析 Laravel 服務容器,轉載請注明出處。喜歡的朋友不要吝嗇你們的贊同,謝謝。 之前在 深度挖掘 Laravel 生命周期 一文中,我們有去探究 Laravel 究竟是如何接收 HTTP 請求,又是如何生成響應并最終呈現給用戶的工作原理。 本章將帶領大...
摘要:的核心概念包括服務容器服務提供者門面契約。所有服務提供者都需要繼承類。可以為服務提供者的方法設置類型提示。方法將在所有其他服務提供者均已注冊之后調用。同樣會整理成思維導圖的形式以方便記憶與回顧。 showImg(https://segmentfault.com/img/remote/1460000010771201); Laravel 的核心概念包括:服務容器、服務提供者、門面(Fac...
摘要:判斷是否存在構造函數,不存在直接實例化,存在則通過來獲取輸入函數,并有相應的方法解決依賴參數問題,實現依賴注入。 Laravel 框架關鍵技術解析·讀書筆記(一) 第一章 入口文件 請求訪問的入口文件,主要完成幾部分工作,分別是: 自動加載函數的添加 服務器實例化與服務注冊 路由加載 請求實例化與路由分發 相應生成與發送 其中,自動加載函數用于包含引用文件,改文件是composer...
摘要:服務容器在說容器之前,我們需要了解什么是容器。服務容器是一個用于管理類依賴和執行依賴注入的強大工具。幾乎所有的服務容器綁定都是在服務提供者中完成,也就是在服務提供者中綁定。 服務容器 在說 Ioc 容器之前,我們需要了解什么是 Ioc 容器。 Laravel 服務容器是一個用于管理類依賴和執行依賴注入的強大工具。 在理解這句話之前,我們需要先了解一下服務容器的來龍去脈: larave...
閱讀 1768·2021-10-11 10:57
閱讀 2352·2021-10-08 10:14
閱讀 3393·2019-08-29 17:26
閱讀 3340·2019-08-28 17:54
閱讀 3021·2019-08-26 13:38
閱讀 2885·2019-08-26 12:19
閱讀 3608·2019-08-23 18:05
閱讀 1277·2019-08-23 17:04