1. 程式人生 > >PHPer:Laravel框架學習服務容器繫結與解析

PHPer:Laravel框架學習服務容器繫結與解析

在這裡插入圖片描述 1.在服務容器中註冊類(bind)

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

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

$sender = this>app>make(sender);//this->app->make('sender');//從服務容器(

this->app)建立一個sender類。在這種情況下,將返回MailSender的例項。 這是服務容器最簡單的使用,下面是對服務容器的詳細介紹

laravel容器基本認識

一開始,index.php 檔案載入 Composer 生成定義的自動載入器,然後從 bootstrap/app.php 指令碼中檢索 Laravel 應用程式的例項。Laravel 本身採取的第一個動作是建立一個 application/ service container 的例項。

KaTeX parse error: Expected group after '_' at position 53: …tion( dirname(_̲_DIR__) ); 這個檔案…

app即是laravel框架的應用程式例項,它在整個請求生命週期都是唯一的。laravel提供了很多服務,包括認證,資料庫,快取,訊息佇列等等,$app作為一個容器管理工具,負責幾乎所有服務元件的例項化以及例項的生命週期管理。當需要一個服務類來完成某個功能的時候,僅需要通過容器解析出該型別的一個例項即可。從最終的使用方式來看,laravel容器對服務例項的管理主要包括以下幾個方面:

服務的繫結與解析 服務提供者的管理 別名的作用 依賴注入 先了解如何在程式碼中獲取到容器例項,再學習上面四個關鍵

如何在程式碼中獲取到容器例項

第一種是

$app = app();//app這個輔助函式定義在endorlaravelrameworksrcIlluminateFoundationhelper.php裡面,,這個檔案定義了很多help函式,並且會通過composer自動載入到專案中。 所以,在參與http請求處理的任何程式碼位置都能夠訪問其中的函式,比如app()。 第二種是

Route::get(’/’, function () { dd(App::basePath()); return ‘’; });//這個其實是用到Facade,中文直譯貌似叫門面,在config/app.php中,有一節陣列aliases專門用來配置一些型別的別名,第一個就是’App’ => IlluminateSupportFacadesApp::class, 具體的Google一下laravel有關門面的具體實現方式 第三種是

在服務提供者裡面直接使用this>applaravelIlluminateSupportServiceProviderthis->app。服務提供者後面還會介紹,現在只是引入。因為服務提供者類都是由laravel容器例項化的,這些類都繼承自IlluminateSupportServiceProvider,它定義了一個例項屬性app:

abstract class ServiceProvider{ protected app;laravellaravelapp; laravel在例項化服務提供者的時候,會把laravel容器例項注入到這個app上面。所以我們在服務提供者裡面,始終能通過this>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, KaTeX parse error: Expected '}', got 'EOF' at end of input: …ncrete = null){this->bind($abstract, $concrete, true); } 在繫結的時候,我們可以直接繫結已經初始化好的資料(基本型別、陣列、物件例項),還可以用匿名函式來繫結。用匿名函式的好處在於,這個服務繫結到容器以後,並不會立即產生服務最終的物件,只有在這個服務解析的時候,匿名函式才會執行,此時才會產生這個服務對應的服務例項。

實際上,當我們使用singleton,bind方法以及陣列形式,(這三個方法是後面要介紹的繫結的方法),進行服務繫結的時候,如果繫結的服務形式,不是一個匿名函式,也會在laravel內部用一個匿名函式包裝起來,這樣的話, 不輪繫結什麼內容,都能做到前面介紹的懶初始化的功能,這對於容器的效能是有好處的。這個可以從bind的原始碼中看到一些細節:

if (! $concrete instanceof Closure) { $concrete = this>getClosure(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(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(this->getAlias(abstract); needsContextualBuild=!empty(needsContextualBuild = ! empty(parameters) || ! is_null( this>getContextualConcrete(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[this->instances[abstract]) && ! $needsContextualBuild) { return this>instances[this->instances[abstract]; } $this->with[] = $parameters; $concrete = this>getConcrete(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(this->isBuildable(concrete, $abstract)) { $object = this>build(this->build(concrete); } else { $object = this>make(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(this->getExtenders(abstract) as $extender) { $object = extender(extender(object, KaTeX parse error: Expected 'EOF', got '}' at position 8: this); }̲ // If the requ…this->isShared($abstract) && ! $needsContextualBuild) { this>instances[this->instances[abstract] = $object; } this>fireResolvingCallbacks(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[this->resolved[abstract] = true; array_pop($this->with); return $object; } 第一步:

needsContextualBuild=!empty(needsContextualBuild = ! empty(parameters) || ! is_null( this>getContextualConcrete(this->getContextualConcrete(abstract) ); 該方法主要是區分,解析的物件是否有引數,如果有引數,還需要對引數做進一步的分析,因為傳入的引數,也可能是依賴注入的,所以還需要對傳入的引數進行解析;這個後面再分析。

第二步:

if (isset(this>instances[this->instances[abstract]) && ! $needsContextualBuild) { return this>instances[this->instances[abstract]; } 如果是繫結的單例,並且不需要上面的引數依賴。我們就可以直接返回 this>instances[this->instances[abstract]。

第三步:

$concrete = this>getConcrete(this->getConcrete(abstract); …/**

  • Get the concrete type for a given abstract.
  • @param string $abstract
  • @return mixed concrete/protectedfunctiongetConcrete(concrete */protected function getConcrete(abstract){ if (! is_null($concrete = this>getContextualConcrete(this->getContextualConcrete(abstract))) { return KaTeX parse error: Expected 'EOF', got '}' at position 11: concrete; }̲ // If we don't…this->bindings[$abstract])) { return this>bindings[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(this->isBuildable(concrete, $abstract)) { $object = this>build(this->build(concrete); } else { $object = this>make(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(this->build(concrete),否則,表示存在巢狀依賴的情況,則採用遞迴的方法執行 this>make(this->make(concrete),直到所有的都解析完為止。

this>build(this->build(concrete)

/**