閱讀時長:5分鐘
技術預備:熟悉Laravel的使用
容器(Container)
一、什麼是容器呢?
容器這個詞估計使用過Laravel的童鞋們肯定不陌生了,但是日常業務開發好像都沒見過它,我們今天就來看看他到底是幹什麼用的,本篇使用Laravel的精簡版Lumen進行舉例。
首先他出現在哪裡呢?
我們可以從入口檔案找到他,就是這個$app
,那麼這個$app
到底是什麼?我們繼續深入看看
在app.php資料夾中可以看到,這個$app
就是Laravel\Lumen\Application::class,而這個類繼承了Container類,所以說Application類就是Laravel中的容器了。
“趙童鞋,究竟什麼是容器呢?"
Laravel中的容器其實就是整個框架的核心,在上圖的index.php中可以看到,一個Request進來後,index.php其實只是呼叫了$app->run()
方法。
之後的URL解析、中介軟體處理、依賴注入,直到請求進入業務方法裡,通通都是容器在進行處理。
二、容器是怎麼運作的呢?
像上述所說,Laravel框架中的大部分功能都是在容器中實現的,而最常用的功能就有服務提供者。
我們這裡就通過呼叫DB的Facade類來說明一下容器中的服務提供者是怎麼運作的。
首先可以看到,在Application中,"db" 繫結了registerDatabaseBindings()
這個方法。
在registerDatabaseBindings()
方法中,使用了框架提供的singleton()
註冊了一個關聯“db”的匿名函式,而這個匿名函式就是“db”的解析器。
在前面的篇章我們講到,當我們通過靜態呼叫DB類的時候,Laravel的Facade模式就通過呼叫resolveFacadeInstance()
去容器中解析出“db”的實際物件。
static::$app[$name]
的寫法去獲取。
因為Laravel中的Container實現了ArrayAccess,所以實際呼叫會進入到Container的offsetGet()
方法中,也就是進入了make()
方法,而因為容器的實際物件是Application類,所以會先進入Application的make()
方法。
在make方法中,終於出現了前面說到的$availableBindings
,也就是說,在這裡會呼叫前面的registerDatabaseBindings()
方法,註冊“db”的解析器。再進行呼叫父類,也就是Container的make()
方法。
而make()方法又跳進了resolve()
中。
(不得不說,當初第一次看框架程式碼時我也被繞暈了,習慣就好)
在resolve()
這裡,容器會先去$this->instances
中查詢,也就是一個用於存放已例項化的物件的陣列。
當獲取不到的時候,就會通過$this->getConcrete()
獲取到我們前面註冊的“db”的解析器,也就是呼叫文章最前面registerDatabaseBindings()
中的匿名函式,獲取到“db”的例項,最終返回給前面的DB靜態呼叫。
(至於匿名函式解析器是怎麼工作的,感興趣的童鞋可以去看看官方文件的loadComponent、register流程,親自去看一遍原始碼印象更深刻)
三、容器有什麼用呢?
通過上面的描述,我們可以知道容器中是有一個陣列instances[]
儲存著已例項化了的物件的,也就是單例模式。
而沒有例項化的物件也是有著類似於解析器之類的關聯機制。
這種做法最大的好處就是可以減少物件生成的開銷,例如某個介面需要使用到Redis和DB,那麼框架就會去呼叫解析器將這兩個物件解析出來,並且儲存在陣列中以便於後續的邏輯中進行復用。
而當某個介面不需要Redis和DB的時候,就可以節省下這兩個物件的開銷,並不需要修改一句程式碼。
四、結語
本次的淺談容器到這裡就結束了,如果覺得被繞暈了的話,說明真的認真看了o( ̄▽ ̄)d。
千萬別灰心,親自去跟著原始碼走一趟,這種被繞暈的感覺就會慢慢消失了。
----- End -----
更多好文
請掃描下面二維碼
歡迎關注~