佳木斯湛栽影视文化发展公司

主頁 > 知識庫 > 詳解Laravel服務(wù)容器的綁定與解析

詳解Laravel服務(wù)容器的綁定與解析

熱門標簽:銀行業(yè)務(wù) 團購網(wǎng)站 科大訊飛語音識別系統(tǒng) 服務(wù)器配置 阿里云 電子圍欄 Mysql連接數(shù)設(shè)置 Linux服務(wù)器

前言

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

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

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

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

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

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

$sender = $this->app->make('sender');
//從服務(wù)容器($this->app)創(chuàng)建一個sender類。

在這種情況下,將返回MailSender的實例。

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

laravel容器基本認識

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

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

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

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

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

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

第一種是

$app = app();
//app這個輔助函數(shù)定義在\vendor\laravel\framework\src\Illuminate\Foundation\helper.php

里面,,這個文件定義了很多help函數(shù),并且會通過composer自動加載到項目中。

所以,在參與http請求處理的任何代碼位置都能夠訪問其中的函數(shù),比如app()。

第二種是

Route::get('/', function () {
  dd(App::basePath());
  return '';
});
//這個其實是用到Facade,中文直譯貌似叫門面,在config/app.php中,

有一節(jié)數(shù)組aliases專門用來配置一些類型的別名,第一個就是'App' => Illuminate\Support\Facades\App::class,

具體的Google一下laravel有關(guān)門面的具體實現(xiàn)方式

第三種是

  在服務(wù)提供者里面直接使用$this->app。服務(wù)提供者后面還會介紹,現(xiàn)在只是引入。因為服務(wù)提供者類都是由laravel容器實例化的,這些類都繼承自Illuminate\Support\ServiceProvider,它定義了一個實例屬性$app:

abstract class ServiceProvider
{
  protected $app;

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

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

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

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

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

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

  在綁定的時候,我們可以直接綁定已經(jīng)初始化好的數(shù)據(jù)(基本類型、數(shù)組、對象實例),還可以用匿名函數(shù)來綁定。用匿名函數(shù)的好處在于,這個服務(wù)綁定到容器以后,并不會立即產(chǎn)生服務(wù)最終的對象,只有在這個服務(wù)解析的時候,匿名函數(shù)才會執(zhí)行,此時才會產(chǎn)生這個服務(wù)對應(yīng)的服務(wù)實例。

  實際上,當我們使用singleton,bind方法以及數(shù)組形式,(這三個方法是后面要介紹的綁定的方法),進行服務(wù)綁定的時候,如果綁定的服務(wù)形式,不是一個匿名函數(shù),也會在laravel內(nèi)部用一個匿名函數(shù)包裝起來,這樣的話, 不輪綁定什么內(nèi)容,都能做到前面介紹的懶初始化的功能,這對于容器的性能是有好處的。這個可以從bind的源碼中看到一些細節(jié):

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

看看bind的底層代碼

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

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

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

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

綁定大概就這些,接下來看解析,也就是取出來用

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

  這個方法接收兩個參數(shù),第一個是服務(wù)的綁定名稱和服務(wù)綁定名稱的別名,如果是別名,那么就會根據(jù)服務(wù)綁定名稱的別名配置,找到最終的服務(wù)綁定名稱,然后進行解析;第二個參數(shù)是一個數(shù)組,最終會傳遞給服務(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ū)分,解析的對象是否有參數(shù),如果有參數(shù),還需要對參數(shù)做進一步的分析,因為傳入的參數(shù),也可能是依賴注入的,所以還需要對傳入的參數(shù)進行解析;這個后面再分析。

第二步:

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;
}

  這一步主要是先從綁定的上下文找,是不是可以找到綁定類;如果沒有,則再從 $bindings[] 中找關(guān)聯(lián)的實現(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 是個閉包,則執(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 \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.
  // 如果傳入的是閉包,則直接執(zhí)行閉包函數(shù),返回結(jié)果
  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;
  // 獲取構(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ù),也就意味著不需要做對應(yīng)的上下文依賴解析。
  if (is_null($constructor)) {
    // 將 build 過程的內(nèi)容 pop,然后直接構(gòu)造對象輸出。
    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.
  // 解析出所有上下文依賴對象,帶入函數(shù),構(gòu)造對象輸出
  $instances = $this->resolveDependencies(
    $dependencies
  );
  array_pop($this->buildStack);
  return $reflector->newInstanceArgs($instances);
}

總結(jié)

以上所述是小編給大家介紹的Laravel服務(wù)容器的綁定與解析,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
如果你覺得本文對你有幫助,歡迎轉(zhuǎn)載,煩請注明出處,謝謝!

您可能感興趣的文章:
  • Laravel服務(wù)容器綁定的幾種方法總結(jié)
  • 詳解如何實現(xiàn)Laravel的服務(wù)容器的方法示例
  • laravel ajax curd 搜索登錄判斷功能的實現(xiàn)
  • Laravel中Kafka的使用詳解
  • laravel使用redis隊列實例講解
  • Laravel的加密解密與哈希實例講解
  • Laravel中10個有用的用法小結(jié)
  • 詳解Laravel服務(wù)容器的優(yōu)勢

標簽:衡水 蚌埠 萍鄉(xiāng) 江蘇 棗莊 大理 衢州 廣元

巨人網(wǎng)絡(luò)通訊聲明:本文標題《詳解Laravel服務(wù)容器的綁定與解析》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266
    仁布县| 呼图壁县| 万盛区| 阳江市| 汤原县| 临颍县| 永胜县| 岳阳县| 吉安市| 武义县| 准格尔旗| 日照市| 高碑店市| 盐城市| 贵溪市| 怀宁县| 武义县| 太仆寺旗| 十堰市| 浪卡子县| 拜城县| 永清县| 罗甸县| 连云港市| 汪清县| 仁寿县| 金塔县| 广河县| 石林| 德州市| 镇平县| 全州县| 古丈县| 佳木斯市| 双辽市| 武功县| 阳谷县| 乡宁县| 锡林郭勒盟| 纳雍县| 嘉善县|