laravel學習:容器綁定與解析


1.在服務容器中注冊類(bind)

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

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

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

  這是服務容器最簡單的使用,下面是對服務容器的詳細介紹

laravel容器基本認識

  一開始,index.php 文件加載 Composer 生成定義的自動加載器,然后從 bootstrap/app.php 腳本中檢索 Laravel 應用程序的實例。Laravel 本身采取的第一個動作是創建一個 application/ service container 的實例。

$app = new Illuminate\Foundation\Application(    dirname(__DIR__) );

  這個文件在每一次請求到達laravel框架都會執行,所創建的$app即是laravel框架的應用程序實例,它在整個請求生命周期都是唯一的。laravel提供了很多服務,包括認證,數據庫,緩存,消息隊列等等,$app作為一個容器管理工具,負責幾乎所有服務組件的實例化以及實例的生命周期管理。當需要一個服務類來完成某個功能的時候,僅需要通過容器解析出該類型的一個實例即可。從最終的使用方式來看,laravel容器對服務實例的管理主要包括以下幾個方面:

  • 服務的綁定與解析

  • 服務提供者的管理

  • 別名的作用

  • 依賴注入

先了解如何在代碼中獲取到容器實例,再學習上面四個關鍵

如何在代碼中獲取到容器實例

第一種是

$app = app();//app這個輔助函數定義在\vendor\laravel\framework\src\Illuminate\Foundation\helper.php里面,,這個文件定義了很多help函數,並且會通過composer自動加載到項目中。 所以,在參與http請求處理的任何代碼位置都能夠訪問其中的函數,比如app()。

第二種是

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

第三種是

  在服務提供者里面直接使用$this->app。服務提供者后面還會介紹,現在只是引入。因為服務提供者類都是由laravel容器實例化的,這些類都繼承自Illuminate\Support\ServiceProvider,它定義了一個實例屬性$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 \Illuminate\Contracts\Container\BindingResolutionException */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); }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM