Laravel 大將之 服務容器 模塊

簡介

服務容器是一個用於管理類依賴和執行依賴注入的強大工具。是整個框架的核心;php

幾乎全部的服務容器綁定都是在服務提供者中完成。bootstrap

框架調用分析

在框架直接生成服務容器的只有一處,在bootstrap/app.php,經過require引用會返回服務容器實例。經過require引用有兩處,一處是public/index.php,服務器訪問的入口;另外一處是tests/CreatesApplication.php,是單元測試的入口;數組

若是想在項目各處中調用,能夠調用$app = Illuminate\Container\Container::getInstance()或者全局幫助函數app()獲取服務容器實例(也就是Illuminate\Foundation/Application實例);服務器

Illuminate\Foundation/Application是對Illuminate\Container\Container的又一層封裝;閉包

Application初始化

那麼實例化Illuminate\Foundation/Application時,作了什麼呢?app

第一步,設置應用的根目錄,並同時註冊核心目錄到服務容器中;核心的目錄有如下框架

  • path:目錄app的位置ide

  • path.base:項目根目錄的位置函數

  • path.lang:目錄resources/lang的位置工具

  • path.config:目錄config的位置

  • path.public:目錄public的位置

  • path.storage:目錄storage的位置

  • path.database:目錄database的位置

  • path.resources:目錄resources的位置

  • path.bootstrap:目錄bootstrap的位置

第二步,將當前Illuminate\Foundation/Application實例保存到$instance類變量,並同時綁定到服務容器做單例綁定,綁定名爲appContainer::class

第三步,順序分別執行註冊Illuminate\Events\EventServiceProviderIlluminate\Log\LogServiceProviderIlluminate\Routing\RoutingServiceProvider三個服務提供者;

註冊服務提供者的順序以下:

  • 若是類變量$serviceProviders已經存在該服務提供者而且不須要強制從新註冊,則返回服務提供者實例$provider

  • 未註冊過當前服務提供者,則繼續執行如下;

  • 若是存在register方法,執行服務提供者的register方法;

  • 將當前服務提供者$provider實例保存到類變量$serviceProviders數組中,同時標記類變量$loadedProviders[get_class($provider)]的值爲true

  • 判斷類變量$booted是否爲true,若是是true,則執行服務提供者的boot方法;(類變量$booted應該是標誌是否全部服務提供者均註冊,框架是否啓動)

第四步,註冊核心類別名;
好比\Illuminate\Foundation\Application::class\Illuminate\Contracts\Container\Container::class起別名爲app

單元測試Applicationbootstrap啓動分析

啓動代碼很簡潔,

public function createApplication() {
    // require 初始化分析上面已經介紹了
    $app = require __DIR__.'/../bootstrap/app.php';
    // 生成`Illuminate\Foundation\Console\Kernel`實例,執行bootstrap方法,完成啓動
    $app->make(Kernel::class)->bootstrap();
    return $app;
}

構造函數主要乾了一件事,註冊一個booted完成後的回調函數,函數執行的內容爲「註冊 Schedule實例到服務提供者,同時加載用戶定義的Schedule任務清單」;

bootstrap方法的執行內容以下:

  1. 加載Illuminate/Foundation/Console/Kernel$bootstrappers變量數組中的類,執行它們的bootstrap方法;

    protected $bootstrappers = [
        // 加載 .env 文件
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
        // 加載 config 目錄下的配置文件
        \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
        // 自定義錯誤報告,錯誤處理方法及呈現
        \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
        // 爲 config/app.php 中的 aliases 數組註冊類別名
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        // 在服務容器中單例綁定一個 request 對象,控制檯命令會用到
        \Illuminate\Foundation\Bootstrap\SetRequestForConsole::class,
        // 註冊 config\app.php 中的 providers 服務提供者
        \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
        // 項目啓動,執行每一個 ServiceProvider 的 boot 方法,
        \Illuminate\Foundation\Bootstrap\BootProviders::class,
    ];
  2. 加載延遲的服務提供者;

Http訪問Applicationbootstrap啓動分析

啓動入口文件在public\index.php

$app = require_once __DIR__.'/../bootstrap/app.php';

// 實例化 Illuminate/Foundation/Http/Kernel 對象
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

// 中間件處理、業務邏輯處理
$response = $kernel->handle(
    // 根據 Symfony 的 request 對象封裝出 Illuminate\Http\Request
    $request = Illuminate\Http\Request::capture() 
);

$response->send();

// 執行全部中間件的 terminate 方法,執行 Application 中的 terminatingCallbacks 回調函數
$kernel->terminate($request, $response);

重要的類變量數組

aliases數組

維護 類與別名 的數組;鍵名爲 類的全限定類名,鍵值爲 數組,每個元素都是該類的別名;

判斷指定類是否有別名:app()->isAlias($name)

獲取指定類的別名:app()->getAlias($abstract)

abstractAliases數組

維護 類與別名 的數組;鍵名爲 別名,鍵值爲 類的全限定類名;

instances數組

維護 類與實例的數組;鍵名爲 類的全限定類名,鍵值爲該類的實例;

移除綁定類:app()->forgetInstance($abstract);

移除全部綁定類:app()->forgetInstances();

bindings數組

經過 bind 方法實現 接口類與實現的綁定;

獲取bindings數組中的內容:app()->getBindings()

resolved數組

鍵名爲 類的全限定類名,鍵值爲布爾值類型(true表示已解析過,false表示未解析過);

with數組

resolved過程當中,會有一些參數;with數組就是參數棧,開始解析時將參數入棧,結束解析時參數出棧;

contextual數組

上下文綁定數組;第一維數組鍵名爲 場合類(好比某個Controller類的類名),第二維數組鍵名爲 抽象類(須要實現的接口類),鍵值爲 Closure 或 某個具體類的類名;

tags數組

維護 標籤與類 的數組;鍵名是 標籤名,鍵值是 對應要綁定的類的名稱;

若是調用tagged方法,會將鍵值數組中的類都make出來,並以數組形式返回;

extenders數組

makeresolve出對象的時候,會執行

foreach ($this->getExtenders($abstract) as $extender) {
    $object = $extender($object, $this);
}

能對解析出來的對象進行修飾;

methodBindings數組

能夠綁定類的一個方法到自定義的函數;用法能夠參考Laravel核心——Ioc服務容器 中的相關章節;

向容器綁定方法與及實現:app()->bindMethod($method, $callback)

判斷容器內是否有指定方法的實現:app()->hasMethodBinding($method)
執行方法的實現:app()->callMethodBinding($method, $instance)或者app()->call($method)

buildStack數組

調用build方法時維護的棧,棧中存放的是當前要new的類名;

reboundCallbacks數組

當調用rebound函數時,會觸發rebound中爲此$abstract設置的回調函數;

註冊入口:app()->rebinding($abstract, Closure $callback);

serviceProviders數組

已在系統註冊的服務提供者ServiceProvider

數組內存放的是loadedProviders鍵名對應類的實例;

loadedProviders數組

系統已加載的ServiceProvider的集合;鍵名爲ServiceProvider的全限定類名,鍵值爲布爾值(true表示已加載,false表示未加載);

獲取延遲加載對象:app()->getLoadedProviders();

deferredServices數組

有些服務提供者是會延遲加載的;這時候會將這些服務提供者聲明的服務登陸在deferredServices數組,鍵名爲延遲加載對象名 ,鍵值爲該延遲加載對象所在的ServiceProvider

獲取延遲加載對象:app()->getDeferredServices();

bootingCallbacks數組

項目啓動前執行的回調函數;(項目啓動是在執行\Illuminate\Foundation\Bootstrap\BootProviders::class的時候)

註冊入口:app()->booting($callback);

bootedCallbacks數組

項目啓動後執行的回調函數;(項目啓動是在執行\Illuminate\Foundation\Bootstrap\BootProviders::class的時候)

註冊入口:app()->booted($callback);

resolvingCallbacks數組

解析時回調函數集合;鍵名爲 類名, 鍵值爲 回調函數數組,每個元素都是回調函數;

註冊入口:app()->resolving($abstract, $callback);

afterResolvingCallbacks數組

解析後回調函數集合;鍵名爲 類名, 鍵值爲 回調函數數組,每個元素都是回調函數;

註冊入口:app()->afterResolving($abstract, $callback);

globalResolvingCallbacks數組

全局解析時回調函數集合;每一次resolve方法調用時都會執行的回調函數集合;

註冊入口:app()->resolving($callback);

globalAfterResolvingCallbacks數組

全局解析後回調函數集合;每一次resolve方法調用後都會執行的回調函數集合;

註冊入口:app()->afterResolving($callback);

terminatingCallbacks數組

系統在返回response以後,會執行terminate方法,來作應用結束前的掃尾處理;

這個數組就是執行terminate方法時會執行的回調函數集合;

註冊入口:app()->terminating(Closure $callback);

經常使用方法的解析

bind方法

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

第一個參數是要註冊的類名或接口名,第二個參數是返回類的實例的閉包(或類的實例類名),第三個參數是不是單例;

方法內部流程:

  1. unsetinstancesaliases 數組中鍵值爲 $abstract 的元素;

  2. 若是 $concrete 值爲 null ,將 $abstract 賦值給 $concrete

  3. 若是 $concrete 不是 Closure 對象,則封裝成閉包;

  4. $concrete$shared 經過 compact,添加進 bindings 數組,鍵名爲 $abstract

  5. 判斷 $abstractresolvedinstances 數組中是否存在,若是存在,則執行第 6 步;

  6. 觸發 rebound回調函數;若是 reboundCallbacks 數組中註冊以 $abstract 爲鍵名的回調函數,則執行這些回調函數;

涉及數組:instancesaliases(unset 操做)、bindings(add 操做)

singleton方法

單例綁定;

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

涉及數組:instancesaliases(unset 操做)、bindings(add 操做)

bindIf方法

單例綁定;

public function bindIf($abstract, $concrete = null, $shared = false) {
    if (! $this->bound($abstract)) {
        $this->bind($abstract, $concrete, $shared);
    }
}

涉及數組:instancesaliases(unset 操做)、bindings(add 操做)

instance方法

綁定實例;

public function instance($abstract, $instance)

方法內部流程:

  1. 若是$abstractaliases數組中存在,則從abstractAliases中全部的值數組中移除該類;

  2. unsetaliases 數組中鍵名爲 $abstract的元素;

  3. 賦值操做:$this->instances[$abstract] = $instance;

  4. 判斷 $abstractresolvedinstances 數組中是否存在,若是存在,則執行第 5 步;

  5. 觸發 rebound回調函數;若是 reboundCallbacks 數組中註冊以 $abstract 爲鍵名的回調函數,則執行這些回調函數;

涉及數組:instances(add 操做)、aliasesabstractAliases(unset 操做)

make方法

public function make($abstract) {
    return $this->resolve($abstract);
}

alias方法

給類起別名;

public function alias($abstract, $alias) {
    $this->aliases[$alias] = $abstract;
    
    $this->abstractAliases[$abstract][] = $alias;
}

涉及數組:aliasesabstractAliases(add 操做)

擴展閱讀

相關文章
相關標籤/搜索