成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Laravel核心——Ioc服務(wù)容器源碼解析(服務(wù)器綁定)

imtianx / 3310人閱讀

摘要:服務(wù)容器的綁定綁定歡迎關(guān)注我的博客綁定是服務(wù)容器最常用的綁定方式,在上一篇文章中我們討論過,的綁定有三種綁定自身綁定閉包綁定接口今天,我們這篇文章主要從源碼上講解服務(wù)容器是如何進(jìn)行綁定的。將閉包函數(shù)和單例變量存入數(shù)組中,以備解析時(shí)使用。

服務(wù)容器的綁定 bind 綁定

歡迎關(guān)注我的博客:www.leoyang90.cn

bind 綁定是服務(wù)容器最常用的綁定方式,在 上一篇文章中我們討論過,bind 的綁定有三種:

綁定自身

綁定閉包

綁定接口

今天,我們這篇文章主要從源碼上講解 Ioc 服務(wù)容器是如何進(jìn)行綁定的。

/**
* Register a binding with the container.
*
* @param string|array $abstract
* @param Closure|string|null $concrete
* @param bool $shared
* @return void
*/
public function bind($abstract, $concrete = null, $shared = false)
{
  // If no concrete type was given, we will simply set the concrete type to the
  // abstract type. After that, the concrete type to be registered as shared
  // without being forced to state their classes in both of the parameters.
  $this->dropStaleInstances($abstract);

  if (is_null($concrete)) {
    $concrete = $abstract;
  }

  // If the factory is not a Closure, it means it is just a class name which is
  // bound into this container to the abstract type and we will just wrap it
  // up inside its own Closure to give us more convenience when extending.
  if (! $concrete instanceof Closure) {
    $concrete = $this->getClosure($abstract, $concrete);
  }

  $this->bindings[$abstract] = compact("concrete", "shared");

  // If the abstract type was already resolved in this container we"ll fire the
  // rebound listener so that any objects which have already gotten resolved
  // can have their copy of the object updated via the listener callbacks.
  if ($this->resolved($abstract)) {
    $this->rebound($abstract);
  }
}

從源碼中我們可以看出,服務(wù)器的綁定有如下幾個(gè)步驟:

去除原有注冊(cè)。去除當(dāng)前綁定接口的原有實(shí)現(xiàn)單例對(duì)象,和原有的別名,為實(shí)現(xiàn)綁定新的實(shí)現(xiàn)做準(zhǔn)備。

加裝閉包。如果實(shí)現(xiàn)類不是閉包(綁定自身或者綁定接口),那么就創(chuàng)建閉包,以實(shí)現(xiàn) lazy 加載。

注冊(cè)。將閉包函數(shù)和單例變量存入 bindings 數(shù)組中,以備解析時(shí)使用。

回調(diào)。如果綁定的接口已經(jīng)被解析過了,將會(huì)調(diào)用回調(diào)函數(shù),對(duì)已經(jīng)解析過的對(duì)象進(jìn)行調(diào)整。

去除原有注冊(cè)

dropStaleInstances 用于去除當(dāng)前接口原有的注冊(cè)和別名,這里負(fù)責(zé)清除綁定的 aliases 和單例對(duì)象的 instances,bindings 后面再做修改:

protected function dropStaleInstances($abstract)
{
    unset($this->instances[$abstract], $this->aliases[$abstract]);
}
加裝閉包

getClosure 的作用是為注冊(cè)的非閉包實(shí)現(xiàn)外加閉包,這樣做有兩個(gè)作用:

延時(shí)加載

服務(wù)容器在 getClosure 中為每個(gè)綁定的類都包一層閉包,這樣服務(wù)容器就只有進(jìn)行解析的時(shí)候閉包才會(huì)真正進(jìn)行運(yùn)行,實(shí)現(xiàn)了 lazy 加載的功能。

遞歸綁定

對(duì)于服務(wù)容器來說,綁定是可以遞歸的,例如:

$app->bind(A::class,B::class);
$app->bind(B::class,C::class);
$app->bind(C::class,function(){
    return new C;
})

對(duì)于 A 類,我們直接解析 A 可以得到 B 類,但是如果僅僅到此為止,服務(wù)容器直接去用反射去創(chuàng)建 B 類的話,那么就很有可能創(chuàng)建失敗,因?yàn)?B 類很有可能也是接口,B 接口綁定了其他實(shí)現(xiàn)類,要知道接口是無法實(shí)例化的。

因此服務(wù)容器需要遞歸地對(duì) A 進(jìn)行解析,這個(gè)就是 getClosure 的作用,它把所有可能會(huì)遞歸的綁定在閉包中都用 make 函數(shù),這樣解析 make(A::class) 的時(shí)候得到閉包 make(B::class),make(B::class) 的時(shí)候會(huì)得到閉包 make(C::class),make(C::class) 終于可以得到真正的實(shí)現(xiàn)了。

對(duì)于自我綁定的情況,因?yàn)椴淮嬖谶f歸情況,所以在閉包中會(huì)使用 build 函數(shù)直接創(chuàng)建對(duì)象。(如果仍然使用 make,那就無限循環(huán)了)

protected function getClosure($abstract, $concrete)
{
    return function ($container, $parameters = []) use ($abstract, $concrete) {
        if ($abstract == $concrete) {
            return $container->build($concrete);
        }
        return $container->makeWith($concrete, $parameters);
    };
}
注冊(cè)

注冊(cè)就是向 binding 數(shù)組中添加注冊(cè)的接口與它的實(shí)現(xiàn),其中 compact() 函數(shù)創(chuàng)建包含變量名和它們的值的數(shù)組,創(chuàng)建后的結(jié)果為:

$bindings[$abstract] = [
  "concrete" => $concrete,
  "shared" => $shared
]
回調(diào)

注冊(cè)之后,還要查看當(dāng)前注冊(cè)的接口是否已經(jīng)被實(shí)例化,如果已經(jīng)被服務(wù)容器實(shí)例化過,那么就要調(diào)用回調(diào)函數(shù)。(若存在回調(diào)函數(shù))
resolved() 函數(shù)用于判斷當(dāng)前接口是否曾被解析過,在判斷之前,先獲取了接口的最終服務(wù)名:

public function resolved($abstract)
{
    if ($this->isAlias($abstract)) {
        $abstract = $this->getAlias($abstract);
    }

    return isset($this->resolved[$abstract]) ||
        isset($this->instances[$abstract]);
}
    
public function isAlias($name)
{
    return isset($this->aliases[$name]);
}

getAlias() 函數(shù)利用遞歸的方法獲取別名的最終服務(wù)名稱:

public function getAlias($abstract)
{
    if (! isset($this->aliases[$abstract])) {
        return $abstract;
    }

    if ($this->aliases[$abstract] === $abstract) {
        throw new LogicException("[{$abstract}] is aliased to itself.");
    }

    return $this->getAlias($this->aliases[$abstract]);
}

如果當(dāng)前接口已經(jīng)被解析過了,那么就要運(yùn)行回調(diào)函數(shù):

protected function rebound($abstract)
{
    $instance = $this->make($abstract);

    foreach ($this->getReboundCallbacks($abstract) as $callback) {
        call_user_func($callback, $this, $instance);
    }
}
    
protected function getReboundCallbacks($abstract)
{
    if (isset($this->reboundCallbacks[$abstract])) {
        return $this->reboundCallbacks[$abstract];
    }

    return [];
}

這里面的 reboundCallbacks 從哪里來呢?這就是 Laravel核心——Ioc服務(wù)容器 文章中提到的 rebinding

public function rebinding($abstract, Closure $callback)
{
    $this->reboundCallbacks[$abstract = $this->getAlias($abstract)][] = $callback;
   
    if ($this->bound($abstract)) {
        return $this->make($abstract);
    }
}

值得注意的是: rebinding 函數(shù)不僅綁定了回調(diào)函數(shù),同時(shí)順帶還對(duì)接口abstract進(jìn)行了解析,因?yàn)橹挥薪馕鲞^,下次注冊(cè)才會(huì)調(diào)用回調(diào)函數(shù)。

singleton 綁定

singleton 綁定僅僅是 bind 綁定的一個(gè) shared 為真的形式:

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

不對(duì)接口進(jìn)行解析,直接給接口一個(gè)實(shí)例作為單例對(duì)象。從下面可以看出,主要的工作就是去除接口在abstractAliases 數(shù)組和 aliases 數(shù)組中的痕跡,防止 make 函數(shù)根據(jù)別名繼續(xù)解析下去出現(xiàn)錯(cuò)誤。如果當(dāng)前接口曾經(jīng)注冊(cè)過,那么就調(diào)用回調(diào)函數(shù)。

public function instance($abstract, $instance)
{
    $this->removeAbstractAlias($abstract);

    $isBound = $this->bound($abstract);
     
    unset($this->aliases[$abstract]); 
           
    $this->instances[$abstract] = $instance;
        
    if ($isBound) {
        $this->rebound($abstract);
    }
}

protected function removeAbstractAlias($searched)
{
    if (! isset($this->aliases[$searched])) {
       return;
    }

    foreach ($this->abstractAliases as $abstract => $aliases) {
        foreach ($aliases as $index => $alias) {
            if ($alias == $searched) {
                unset($this->abstractAliases[$abstract][$index]);
            }
        }
    }
}

public function bound($abstract)
{
    return isset($this->bindings[$abstract]) ||
           isset($this->instances[$abstract]) ||
           $this->isAlias($abstract);
}
Context 綁定

Context 綁定一般用于依賴注入,當(dāng)我們利用依賴注入來自動(dòng)實(shí)例化對(duì)象時(shí),服務(wù)容器其實(shí)是利用反射機(jī)制來為構(gòu)造函數(shù)實(shí)例化它的參數(shù),這個(gè)過程中,被實(shí)例化的對(duì)象就是下面的 concrete,構(gòu)造函數(shù)的參數(shù)接口是 abstract,參數(shù)接口實(shí)際的實(shí)現(xiàn)是 implementation。
例如:

$this->app->when(PhotoController::class)
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk("local");
          });

這里實(shí)例化對(duì)象 concrete 就是 PhotoController,構(gòu)造函數(shù)的參數(shù)接口 abstract 就是 Filesystem。參數(shù)接口實(shí)際實(shí)現(xiàn) implementation 是 Storage::disk("local")。

這樣,每次進(jìn)行解析構(gòu)造函數(shù)的參數(shù)接口的時(shí)候,都會(huì)去判斷當(dāng)前的 contextual 數(shù)組里面 concrete[concrete] [abstract](也就是 concrete[PhotoController::class] [Filesystem::class])對(duì)應(yīng)的上下文綁定,如果有就直接從數(shù)組中取出來,如果沒有就按照正常方式解析。
值得注意的是,concrete 和 abstract 都是利用 getAlias 函數(shù),保證最后拿到的不是別名。

public function when($concrete)
{
    return new ContextualBindingBuilder($this, $this->getAlias($concrete));
}
public function __construct(Container $container, $concrete)
{
    $this->concrete = $concrete;
    $this->container = $container;
}
public function needs($abstract)
{
    $this->needs = $abstract;

    return $this;
}
public function give($implementation)
{
    $this->container->addContextualBinding(
        $this->concrete, $this->needs, $implementation
    );
}
public function addContextualBinding($concrete, $abstract, $implementation)
{
    $this->contextual[$concrete][$this->getAlias($abstract)] = $implementation;
}
tag 綁定

標(biāo)簽綁定比較簡(jiǎn)單,綁定過程就是將標(biāo)簽和接口之間建立一個(gè)對(duì)應(yīng)數(shù)組,在解析的過程中,按照標(biāo)簽把所有接口都解析一遍即可。

public function tag($abstracts, $tags)
{
    $tags = is_array($tags) ? $tags : array_slice(func_get_args(), 1);

    foreach ($tags as $tag) {
        if (! isset($this->tags[$tag])) {
            $this->tags[$tag] = [];
        }

        foreach ((array) $abstracts as $abstract) {
            $this->tags[$tag][] = $abstract;
        }
    }
}
數(shù)組綁定

利用數(shù)組進(jìn)行綁定的時(shí)候 ($app()[A::class] = B::class),服務(wù)容器會(huì)調(diào)用 offsetSet 函數(shù):

public function offsetSet($key, $value)
{
    $this->bind($key, $value instanceof Closure ? $value : function () use ($value) {
            return $value;
    });
}
extend擴(kuò)展

extend 擴(kuò)展分為兩種,一種是針對(duì)instance注冊(cè)的對(duì)象,這種情況將立即起作用,并更新之前實(shí)例化的對(duì)象;另一種情況是非 instance 注冊(cè)的對(duì)象,那么閉包函數(shù)將會(huì)被放入 extenders 數(shù)組中,將在下一次實(shí)例化對(duì)象的時(shí)候才起作用:

public function extend($abstract, Closure $closure)
{
    $abstract = $this->getAlias($abstract);

    if (isset($this->instances[$abstract])) {
        $this->instances[$abstract] = $closure($this->instances[$abstract], $this);

        $this->rebound($abstract);
    } else {
        $this->extenders[$abstract][] = $closure;
        
        if ($this->resolved()) {
            $this->rebound($abstract);
        }
    }
}
服務(wù)器事件

服務(wù)器的事件注冊(cè)依靠 resolving 函數(shù)和 afterResolving 函數(shù),這兩個(gè)函數(shù)維護(hù)著 globalResolvingCallbacks、resolvingCallbacks、globalAfterResolvingCallbacks、afterResolvingCallbacks 數(shù)組,這些數(shù)組中存放著事件的回調(diào)閉包函數(shù),每當(dāng)對(duì)對(duì)象進(jìn)行解析時(shí)就會(huì)遍歷這些數(shù)組,觸發(fā)事件:

public function resolving($abstract, Closure $callback = null)
{
    if (is_string($abstract)) {
        $abstract = $this->getAlias($abstract);
    }

    if (is_null($callback) && $abstract instanceof Closure) {
        $this->globalResolvingCallbacks[] = $abstract;
    } else {
        $this->resolvingCallbacks[$abstract][] = $callback;
    }
}

public function afterResolving($abstract, Closure $callback = null)
{
    if (is_string($abstract)) {
        $abstract = $this->getAlias($abstract);
    }

    if ($abstract instanceof Closure && is_null($callback)) {
        $this->globalAfterResolvingCallbacks[] = $abstract;
    } else {
        $this->afterResolvingCallbacks[$abstract][] = $callback;
    }
}

Written with StackEdit.

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/22925.html

相關(guān)文章

  • 深入剖析 Laravel 服務(wù)容器

    摘要:劃下重點(diǎn),服務(wù)容器是用于管理類的依賴和執(zhí)行依賴注入的工具。類的實(shí)例化及其依賴的注入,完全由服務(wù)容器自動(dòng)的去完成。 本文首發(fā)于 深入剖析 Laravel 服務(wù)容器,轉(zhuǎn)載請(qǐng)注明出處。喜歡的朋友不要吝嗇你們的贊同,謝謝。 之前在 深度挖掘 Laravel 生命周期 一文中,我們有去探究 Laravel 究竟是如何接收 HTTP 請(qǐng)求,又是如何生成響應(yīng)并最終呈現(xiàn)給用戶的工作原理。 本章將帶領(lǐng)大...

    abson 評(píng)論0 收藏0
  • Laravel思維導(dǎo)圖之Laravel核心概念

    摘要:的核心概念包括服務(wù)容器服務(wù)提供者門面契約。所有服務(wù)提供者都需要繼承類。可以為服務(wù)提供者的方法設(shè)置類型提示。方法將在所有其他服務(wù)提供者均已注冊(cè)之后調(diào)用。同樣會(huì)整理成思維導(dǎo)圖的形式以方便記憶與回顧。 showImg(https://segmentfault.com/img/remote/1460000010771201); Laravel 的核心概念包括:服務(wù)容器、服務(wù)提供者、門面(Fac...

    wthee 評(píng)論0 收藏0
  • 為什么我們需要 Laravel IoC 容器?

    摘要:哲學(xué)的一個(gè)重要組成部分就是容器,也可以稱為服務(wù)容器。那我們要怎么做呢請(qǐng)看下面的例子數(shù)據(jù)庫連接通過上面的代碼,如果我們想把改成,根本不需要去修改類構(gòu)造函數(shù)里的依賴?,F(xiàn)在我要講下容器里到底發(fā)生了什么。 showImg(https://segmentfault.com/img/remote/1460000018868909); IOC 容器是一個(gè)實(shí)現(xiàn)依賴注入的便利機(jī)制 - Taylor?Ot...

    xiaokai 評(píng)論0 收藏0
  • Laravel中的核心概念

    摘要:可以為服務(wù)提供者的方法設(shè)置類型提示。方法將在所有其他服務(wù)提供者均已注冊(cè)之后調(diào)用。所有服務(wù)提供者都在配置文件中注冊(cè)。可以選擇推遲服務(wù)提供者的注冊(cè),直到真正需要注冊(cè)綁定時(shí),這樣可以提供應(yīng)用程序的性能。 本文最早發(fā)布于 Rootrl的Blog 導(dǎo)言 Laravel是一款先進(jìn)的現(xiàn)代化框架,里面有一些概念非常重要。在上手Laravel之前,我認(rèn)為先弄懂這些概念是很有必要的。你甚至需要重溫下PHP...

    ddongjian0000 評(píng)論0 收藏0
  • Laravel核心——Ioc服務(wù)容器源碼解析服務(wù)解析

    摘要:而函數(shù)作用是加載延遲服務(wù),與容器解析關(guān)系不大,我們放在以后再說。在構(gòu)造之前,服務(wù)容器會(huì)先把放入中,繼而再去解析。利用服務(wù)容器解析依賴的參數(shù)。 make解析 首先歡迎關(guān)注我的博客: www.leoyang90.cn 服務(wù)容器對(duì)對(duì)象的自動(dòng)解析是服務(wù)容器的核心功能,make 函數(shù)、build 函數(shù)是實(shí)例化對(duì)象重要的核心,先大致看一下代碼: public function make($abst...

    hearaway 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<