callAfterResolving() 就是結合這兩者的 protected 方法。
本頁將確認 Laravel Framework v13.35.0 的實作,深入探討Laravel 套件開發中從 boot() 進行擴充的方式。這並不是等待整個應用程式啟動完成的掛鉤,而是針對指定服務解析的掛鉤。
尚未解析就等待,已解析就立即執行
ServiceProvider::callAfterResolving() 會執行以下兩個階段的處理。
- 將回呼註冊到容器的
afterResolving()。 - 若
resolved()為 true,就以make()取得實例,並當場呼叫回呼。
make() 目標。在一般的解析流程中,容器會建構物件、套用 extender、呼叫 resolving 回呼,接著才呼叫 afterResolving 回呼。
在一般的 singleton 取得流程中,容器會提前 return 已儲存的實例,因此並不是每次取得都會觸發
afterResolving。重點在於,若只註冊 afterResolving(),註冊前已建立的 singleton 就會缺少設定。
為驗證 Factory 加入規則
以提供套件專屬字串規則為例,在validator 解析後註冊規則。官方的 ValidationServiceProvider 將此鍵註冊為 singleton,而提供者本身支援延遲載入。
extend() 是 Illuminate\Validation\Factory 的規則註冊 API,與後述容器的 extend() 是不同的方法。一般的自訂規則在值為空等情況下可能不會執行,因此必填性請以 required 指定。規則本身的詳細設計請參閱自訂驗證規則。
Factory::extend() 會覆寫相同規則名稱的陣列元素,因此像此範例這樣重複註冊相同的處理,也不會增加項目。不過,為了避免覆寫其他套件的規則,公開的規則名稱應加上套件專屬的前綴。
回呼會在 Factory 解析時執行。規則的驗證閉包則是在之後 Validator 驗證目標值時才執行。註冊掛鉤時並不會驗證輸入值。
讓目標鍵與已解析判定保持一致
在容器一般的解析事件中,除了與已註冊鍵相符的回呼外,與取得物件型別相符的回呼也會被選取。另一方面,callAfterResolving() 當場使用的 resolved($name),則是針對將別名正規化後的鍵,檢查是否有已解析旗標或已儲存的實例。它並不會搜尋所有已建立的物件。
因此,即使某個介面或類別已被解析,以其他鍵呼叫的 resolved() 也不一定會是 true。請確認目標服務實際的綁定與 alias,並以相同的鍵測試已解析的情況。上述範例指定的是官方提供者所註冊的 validator。
此外,resolved() 也包含「過去曾被解析」的判定,並不代表目前的實例一定仍然存在。
這不是保證「只執行一次」的 API
callAfterResolving() 會持續保留已註冊的回呼。即使已當場執行過,之後若有新解析的物件,已註冊的回呼仍會執行。再者,若再次呼叫此方法,回呼本身也會被追加。
特別是一般的非共用綁定已被解析過時需要注意。在確認過的實作中,流程如下。
- 註冊新的回呼。
- 由於
resolved()為 true,因此呼叫make()。 make()建立新的物件,並在其解析事件中執行回呼。- 對於從
make()回傳的同一個物件,也會當場執行回呼。
替換物件請使用其他 API
afterResolving 回呼的回傳值不會用於替換容器回傳的物件。若想回傳裝飾器來替換服務本身,請考慮使用容器的 extend()。此 API 的閉包依約定須回傳變更後的服務。
同樣地,callAfterResolving() 也不是用來更新所有已儲存在其他物件中之依賴的機制。若目的是在重新綁定時更新依賴,請確認官方文件中的 rebinding() 以及目標類別的設計。
真正延遲載入服務的提供者需求,請參閱 DeferrableProvider。使用掛鉤,與將註冊該掛鉤的提供者本身設為延遲載入,是兩回事。
更新時應確認的組合
發布套件前,除了一般的啟動順序外,也要驗證其他提供者先使用了該服務的情況。
若以靜態旗標記錄在整個應用程式中只使用過一次,可能會導致新的容器或在測試中重新建立的服務缺少設定。設定的冪等性應盡可能以目標物件或註冊鍵為單位來確保。
相關頁面
套件 View 的覆寫與更新
確認 loadViewsFrom() 使用解析後掛鉤註冊 View 命名空間的範例。
套件翻譯的覆寫與更新
確認 Translator 的命名空間註冊,以及已載入翻譯的處理方式。