日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Laravel开发:Laravel核心——Ioc服务容器源码解析(服务器绑定)

發布時間:2024/10/12 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Laravel开发:Laravel核心——Ioc服务容器源码解析(服务器绑定) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

服務容器的綁定

bind 綁定

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

  • 綁定自身

  • 綁定閉包

  • 綁定接口

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

/** * 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);} }

  

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

  • 去除原有注冊。去除當前綁定接口的原有實現單例對象,和原有的別名,為實現綁定新的實現做準備。

  • 加裝閉包。如果實現類不是閉包(綁定自身或者綁定接口),那么就創建閉包,以實現 lazy 加載。

  • 注冊。將閉包函數和單例變量存入 bindings 數組中,以備解析時使用。

  • 回調。如果綁定的接口已經被解析過了,將會調用回調函數,對已經解析過的對象進行調整。

  • 去除原有注冊

    dropStaleInstances 用于去除當前接口原有的注冊和別名,這里負責清除綁定的 aliases 和單例對象的 instances,bindings 后面再做修改:

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

      

    加裝閉包

    getClosure 的作用是為注冊的非閉包實現外加閉包,這樣做有兩個作用:

    • 延時加載

    服務容器在 getClosure 中為每個綁定的類都包一層閉包,這樣服務容器就只有進行解析的時候閉包才會真正進行運行,實現了 lazy 加載的功能。

    • 遞歸綁定

    對于服務容器來說,綁定是可以遞歸的,例如:

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

      

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

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

    對于自我綁定的情況,因為不存在遞歸情況,所以在閉包中會使用 build 函數直接創建對象。(如果仍然使用 make,那就無限循環了)

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

      

    注冊

    注冊就是向 binding 數組中添加注冊的接口與它的實現,其中 compact() 函數創建包含變量名和它們的值的數組,創建后的結果為:

    $bindings[$abstract] = ['concrete' => $concrete,'shared' => $shared ]

      

    回調

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

    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() 函數利用遞歸的方法獲取別名的最終服務名稱:

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

      

    如果當前接口已經被解析過了,那么就要運行回調函數:

    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服務容器?文章中提到的 rebinding

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

      

    值得注意的是: rebinding 函數不僅綁定了回調函數,同時順帶還對接口abstract進行了解析,因為只有解析過,下次注冊才會調用回調函數。

    singleton 綁定

    singleton 綁定僅僅是 bind 綁定的一個 shared 為真的形式:public function singleton($abstract, $concrete = null) {$this->bind($abstract, $concrete, true); }

      

    instance 綁定

    不對接口進行解析,直接給接口一個實例作為單例對象。從下面可以看出,主要的工作就是去除接口在abstractAliases 數組和 aliases 數組中的痕跡,防止 make 函數根據別名繼續解析下去出現錯誤。如果當前接口曾經注冊過,那么就調用回調函數。

    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 綁定一般用于依賴注入,當我們利用依賴注入來自動實例化對象時,服務容器其實是利用反射機制來為構造函數實例化它的參數,這個過程中,被實例化的對象就是下面的 concrete,構造函數的參數接口是 abstract,參數接口實際的實現是 implementation。
    例如:

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

      

    這里實例化對象 concrete 就是 PhotoController,構造函數的參數接口 abstract 就是 Filesystem。參數接口實際實現 implementation 是 Storage::disk('local')。

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

    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 綁定

    標簽綁定比較簡單,綁定過程就是將標簽和接口之間建立一個對應數組,在解析的過程中,按照標簽把所有接口都解析一遍即可。

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

      

    數組綁定

    利用數組進行綁定的時候 ($app()[A::class] = B::class),服務容器會調用 offsetSet 函數:

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

      

    extend擴展

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

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

      

    服務器事件

    服務器的事件注冊依靠 resolving 函數和 afterResolving 函數,這兩個函數維護著 globalResolvingCallbacks、resolvingCallbacks、globalAfterResolvingCallbacks、afterResolvingCallbacks 數組,這些數組中存放著事件的回調閉包函數,每當對對象進行解析時就會遍歷這些數組,觸發事件:

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

      

    本文轉自:https://segmentfault.com/a/1190000009369494

    轉載于:https://www.cnblogs.com/cxscode/p/7552041.html

    總結

    以上是生活随笔為你收集整理的Laravel开发:Laravel核心——Ioc服务容器源码解析(服务器绑定)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。