Laravel IoC

FreeMason發表於2019-07-26

入口檔案 index.php

1、載入框架依賴
2、建立 app 容器
3、解析 Http\Kernel.php 核心
4、執行請求操作
5、返回結果
6、終止應用程式

file

app.php 檔案(require_once DIR.'/../bootstrap/app.php')

1、建立容器
2、繫結 Http\Hernel.php 到容器
3、繫結 Console\Kernel.php 到容器
4、繫結 Exception\Hadnler.php 到容器
5、返回 app 容器

file

app 容器

列印 app 容器

1、serviceProviders 屬性
    用於存放所有已經註冊完畢的服務提供者

2、loadedProviders 屬性
    跟 serviceProviders 陣列類似,標記方式不同

3、deferredServices 屬性
    用來儲存所有延遲載入服務提供者

4、resolved 屬性
    服務解析完畢,都會在 resolved 屬性裡面存入一條記錄,表示繫結已經解析過

5、bindings 屬性
    記錄所有繫結

6、instances 屬性
    在第一次解析繫結時,如果 shared 為 true則都會往 instances 屬性裡面存入一條記錄

7、aliases 屬性
    服務繫結的別名,別名數量無限制

file

註冊服務提供者

註冊服務提供者只需要繼承 ServiceProvider 抽象類即可(放在那兒都可以),並在 config/app.php providers 陣列
裡面註冊或使用 app()->register(TestProvider::class)(此方法 $defer 屬性無效) 即可

1、繼承 ServiceProvider 抽象類

2、建立 register 方法並使用 app 容器繫結服務(可繫結任意數量服務)

3、建立 boot 方法,初始化所繫結的服務

4、$defer 屬性為是否延遲載入,為 true 會存入到 deferredServices 屬性,程式註冊時會呼叫 ServicePr
    ovider 抽象類的 isDeferred() 方法 『必須在config/app.php providers中註冊』

5、事件觸發註冊服務提供者,ServiceProvider 抽象類的 when 方法返回一個陣列,陣列裡麵包含事件
    名稱 如:[TestEvent::class],『$defer 必須為 true』

注意:
    PHP 在處理請求前,都會從入口檔案把所有 PHP 檔案都掃描一遍。Laravel 為了效能考慮,會在第一
    次初始化的時候,把所有服務提供者都快取到 bootstrap/cache/service.php 檔案裡面,所以有時候在
    改或增了服務提供者,重新整理可能不生效,這有可能是快取所致,這時把 service.php 與 packages.php
    刪除再重試

註冊服務提供者別名

app()->alias('testProvider','test') 別名數量無限制

file

app 容器繫結

1、app()->bind($abstract, $concrete = null, $shared = false)
第一個引數是服務繫結名稱,第二個引數是繫結結果(引數型別:\Closure|string|null),第三個引數是否共享(類似
單例),預設為 false,第二個引數,如果是非閉包,內部會包裹上閉包,好處是延遲載入,節約空間

file

2、app()->singleton($abstract, $concrete = null)
第一個引數是服務繫結名稱,第一個引數是繫結結果,在內部是呼叫了 app()->bind($abstract, $concrete = null, true),
第三個引數為 true

file

3、app()['test'] = function(){ return 'test' }
使用了 PHP ArrayAccess 介面,在內容呼叫了 app()->bind() 方法,第三個引數為 false

file
file

4、app()->instance('test',例項)
繫結一個例項,跟上面三個比,就是少了一個閉包

app 容器解析

1、app($abstract, $parameters = [])
    第一個引數是服務繫結的名稱或別名,第二個引數為上下文名稱或別名

2、app()->make($abstract, $parameters = [])
    同上

3、app()[$abstract]
    只有第一個引數,使用了 PHP ArrayAccess 介面

DI 依賴注入

DI 是 IOC 的一種實現,DI 是一種設計模式,IOC 是一種設計思想,DI 實現原理是使用 PHP 反射機制來反射出相應依賴對
象名稱,通過 aliases 屬性得到服務名稱,然後從容器解析出服務例項,最後傳入對應方法,這個過程就是所謂的依賴注入

file
file

總結

Laravel 是一個元件式框架,實現了高度解耦,再使用 IOC 容器來管理解耦後的元件,我把這個容器理解為使用了物件的高階註冊樹,當元件越來越多時,那麼這個容器物件不是越來越大嗎?所以 Laravel 使用了閉包來延遲載入,但是有個問題,如果每次獲取都去 new 一次,不是很浪費時間與空間嗎?在能滿足需求的情況下,能不能只 new 一次(類似單例),所以 Laravel 又引入了 shared 來實現單例,用 instances 屬性來儲存單例(服務在生命週期內,shared 不可更改,而且 Laravel 大部分服務 shared 都為 true),現在又有個需求,當某個動作發生時,就觸發服務載入?所以 Laravel 又加入了 when 方法來繫結事件,前提是這個服務 shared 必須為 true。

光年之外