服務容器是一個用於管理類依賴和執行依賴注入的強大工具。是整個框架的核心;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
類變量,並同時綁定到服務容器做單例綁定,綁定名爲app
或Container::class
;
第三步,順序分別執行註冊Illuminate\Events\EventServiceProvider
、Illuminate\Log\LogServiceProvider
和Illuminate\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
;
Application
的bootstrap
啓動分析啓動代碼很簡潔,
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
方法的執行內容以下:
加載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, ];
加載延遲的服務提供者;
Application
的bootstrap
啓動分析啓動入口文件在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
數組在make
或resolve
出對象的時候,會執行
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)
第一個參數是要註冊的類名或接口名,第二個參數是返回類的實例的閉包(或類的實例類名),第三個參數是不是單例;
方法內部流程:
unset
掉 instances
和 aliases
數組中鍵值爲 $abstract
的元素;
若是 $concrete
值爲 null
,將 $abstract
賦值給 $concrete
;
若是 $concrete
不是 Closure
對象,則封裝成閉包;
將 $concrete
和 $shared
經過 compact
,添加進 bindings
數組,鍵名爲 $abstract
;
判斷 $abstract
在 resolved
和 instances
數組中是否存在,若是存在,則執行第 6 步;
觸發 rebound
回調函數;若是 reboundCallbacks
數組中註冊以 $abstract
爲鍵名的回調函數,則執行這些回調函數;
涉及數組:
instances
和aliases
(unset 操做)、bindings
(add 操做)
singleton
方法單例綁定;
public function singleton($abstract, $concrete = null) $this->bind($abstract, $concrete, true); }
涉及數組:
instances
和aliases
(unset 操做)、bindings
(add 操做)
bindIf
方法單例綁定;
public function bindIf($abstract, $concrete = null, $shared = false) { if (! $this->bound($abstract)) { $this->bind($abstract, $concrete, $shared); } }
涉及數組:
instances
和aliases
(unset 操做)、bindings
(add 操做)
instance
方法綁定實例;
public function instance($abstract, $instance)
方法內部流程:
若是$abstract
在aliases
數組中存在,則從abstractAliases
中全部的值數組中移除該類;
unset
掉 aliases
數組中鍵名爲 $abstract
的元素;
賦值操做:$this->instances[$abstract] = $instance;
判斷 $abstract
在 resolved
和 instances
數組中是否存在,若是存在,則執行第 5 步;
觸發 rebound
回調函數;若是 reboundCallbacks
數組中註冊以 $abstract
爲鍵名的回調函數,則執行這些回調函數;
涉及數組:
instances
(add 操做)、aliases
和abstractAliases
(unset 操做)
make
方法public function make($abstract) { return $this->resolve($abstract); }
alias
方法給類起別名;
public function alias($abstract, $alias) { $this->aliases[$alias] = $abstract; $this->abstractAliases[$abstract][] = $alias; }
涉及數組:
aliases
和abstractAliases
(add 操做)