laravel的模塊化是如何實現的
在laravel提供的官方文檔上,有一個這樣的名詞 服務提供者,文檔中介紹了它在laravel框架中的角色,以及如何使用它,但卻沒有講明服務提供者的本質--它是為了解決什么問題而存在的? 不解決這一點,對於它的理解,則只會停留在表面.服務提供者是laravel實現模塊化設計的手法.
為什么要進行模塊化設計這里就不說的,可以參考下這些:模塊化設計 , 模塊化的意義何在?
為了實現模塊化,必然要將一段程序組合起來,完成特定的事,從而形成模塊.在laravel
中, 一個模塊都表現為一個Service
.應用程序主體與組件(模塊)之間必然要通過某種方式連接起來,才能使組件被主體所調用.在laravel中一個Service
會被它的 Service Prorider
注入到ioc中,這樣組件就與主體聯系了起來, 具體的表現形式則是組件被主體所用,主體使用組件完成組件所擅長的事. 源代碼是最好的文檔,接下來,我們就看看"文檔"是怎么說的.
laravel的啟動過程
我們從啟動開始,詳細地分析在整個應用程序的生命周期中,Service Provider
到底是什么?通過閱讀源碼方式來了解Serivce Provider
在laravel中到底做了什么.
laravle的啟動過程做了很多事,這里就不一一敘述了,主要說明有關Sevice Provider
的部分.laravel有兩個入口分別處理不同的請求1:public/index.php(http)
2.php arisan(cli)
.雖然入口有兩種但它們的啟動過程卻相差無幾(具體可以再詳細了解這兩種方式).
基礎啟動
bootstrap/app.php
則是它的主要啟動文件, 在\Illuminate\Foundation\Application::__construct
中就可以看到應用主體的啟動做了什么:
- 綁定Appliation和Container
- 注冊最基礎的Service Provider
- 注冊別名
- 注冊該應用程序的路徑
上面啟動過程的代碼較簡單,代碼不分析了.就說一說為什么要做這幾步,以及這幾步對lavarel有什么作用.
laravel中以ioc為基礎來構建應用程序的,所以,把這個最基礎的Applicatoin放入容器中,供需要時可以隨時從容器中提取使用(container 與 Application的關系看看代碼就清晰了).
一個應用的合理運行必然離不開一些最基本的功能點,就像人類一樣,雖然最重要的是大腦,但同樣也不能沒有血管,心臟.所以在laravel中也同樣存在一些不可或缺的service provider, 比如事件和路由器.\Illuminate\Events\EventServiceProvider
則把構建觀察者的類注入到了ioc中,同時,在laravel源代碼中大量使用了觀察者模式來處理問題,\Illuminate\Routing\RoutingServiceProvider
則把路由相關的服務注入到了主體中,因為一個http請求最不可或缺的當然是路由;
注冊別名,則是為了調用ioc中的類時更方便,還有一點要注意是的,可以為一個類注冊多個別名,為什么呢,其實這多個別名是存在關系的,它們大多是父類與子類的關系,就像Appliation和Container;
注冊應用的路徑則把一樣常用的,跟應用有關的路徑放到ioc中方便取用.啟動的第一步到這就結束了.
http應用請求的啟動
緊接着,又注冊了三個類到ioc中,分別是http處理核心類,cli處理核心類,和異常處理.在這里注冊了兩種不同請親的處理類,所以兩個請求入口所做的事情都差不多了.接下來以http請求為例來,繼續看下去.public/index.php:50
則初始化了\App\Http\Kernel::__construct
,也就初始化了中間件.接着public/index.php:52
則調用了\Illuminate\Foundation\Http\Kernel::handle
來處理請求,進一步看下去,也就是\Illuminate\Foundation\Http\Kernel::bootstrap
中通過Application::bootstrapWith
完成http請求環境的初始化,具體則是:
- 標記Application為已經引導啟動狀態
- 依次啟動
\Illuminate\Foundation\Http\Kernel::$bootstrappers
中的項目
1.Illuminate\Foundation\Bootstrap\DetectEnvironment
:設置應用程序的環境
2.Illuminate\Foundation\Bootstrap\LoadConfiguration
:載入應用程序的配置文件
3.Illuminate\Foundation\Bootstrap\ConfigureLogging
:綁定日志處理類
4.Illuminate\Foundation\Bootstrap\HandleExceptions
:異常處理
5.Illuminate\Foundation\Bootstrap\RegisterFacades
:Facade模式的應用
6.Illuminate\Foundation\Bootstrap\RegisterProviders
:注冊Service Provider
7.Illuminate\Foundation\Bootstrap\BootProviders
:標記啟動完成 , 執行Srvice Provider
的register
方法,完成注入
然后,在vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:129
就進入到了路由器,進而進入對應Controller進行處理,最后完成處理,請求結束.這一部分就不說了.
cli請求的啟動
這一部分略,步驟與http類似
從它的請求生命周期可以看出以下幾點
1:Service Provider
在啟動階段就已經與注入了IOC
2:分為了兩種,一種是由系統控制,而另一種則是可由用戶控制.第一種是構成應用程序的基礎,不能缺少;第二種則將控制權交給用戶,由用戶掌控應用的運行,比如加載第三方composer
包,或自定義的Service
等等.
此時,模塊化已經初現雛形.在Controller中,可以調用或組合不同的Service
, 來完成特定的邏輯,因為它已經在IOC中了;我們可以控制不同的Service
或增或減,自定義這個應用程序的功能,成為新的系統.接下來,再看看Service Provider
做了什么,使模塊化在laravel中更完善.
注冊Service Provider
在注冊基礎的Service Provider
中,不難發現,完成注冊過程的是\Illuminate\Foundation\Application::register
,這個方法比較簡單,一是執行了ServierProceder::register
方法,通過這個接口將Service
注冊動IOC中;二是在系統已經啟動的情況下,執行ServierProceder::boot
方法,作用在這里講的很清楚.
在注冊用戶自定義(config/app.php:124)時,\Illuminate\Foundation\Application::registerConfiguredProviders
則完成了注冊過程,其處理核心\Illuminate\Foundation\ProviderRepository::load
主要做了以下幾步
1:解析所有的Service Provider
, 通過它的defer屬性來決定是否延遲加載
2:將解析的結果,也就是一個Service Provider
的數組,緩存為文件,下次直接載入解析后的緩存文件
3:只加載defer屬性不為ture的Service Provider
, 並注冊
4:將延遲加載的放支容器中(APP::$deferredServices),在需要的時候再加載
從注冊過程可以看出,我們可以定義Service Provider
加載的時間,並不僅僅在啟動這過程中加載,而是在需要的時候再加載.一種是通過\Illuminate\Foundation\Application::make
;還有一種方式延遲加載則是在when方法中通過事件注冊到某項事件上去,這一點則要好好看看\Illuminate\Foundation\ProviderRepository
類了.
理清它的注冊過程后,我們再仔細看看Service Provider
的抽象類,它作為基類,提供了Servicer Provider
能做的所有事情.了解它能更好的理解Service Provider
.這個類並不復雜,唯一一個不是很清晰的只有commands
方法:它將命令注冊到了\Illuminate\Console\Events\ArtisanStarting
事件中,為什么這樣做? 在cli時,啟動時,會有該事件的執行,從而通過Service Provider
把命令注入到了IOC中,這樣執行命令就行雲流水了.
到這里,用Service Provider
來實現模塊化的功能得到了強化,既可以延遲加載,還可以注冊命令.接下來,通過一些例子,看看它具體做了什么.
Service Provider的使用
Service Provider有很多,我們隨便挑兩個來看看
QueueServiceProvider
\Illuminate\Queue\QueueServiceProvider
並非系統控制級別,看源代碼,可以發現,里面有很多注冊方法,注冊了很多有關隊列的類到IOC中,同時也注冊了很多命令.在provides
方法中,它返回的就是一個所有在這個Service Provider
出現的的各個類,它是做什么的?其實我們可以發現,這個Service Provider
是延遲加載的,所以這個Service
所提供的各種服務並不會出現在服務中,上面我們說過,在需要的時候再加載.這一功能就是由provides
中的返回值來決定的,當調用IOC中,那些延遲加載服務中出現的那些類時,再加載這些延遲的服務,從而可以獲得延遲服務所提供的服務.
EventServiceProvider
\Illuminate\Events\EventServiceProvider
是應用程序的核心,它又做了什么呢?
它也是向IOC中注冊了事件處理的類,同時為這個類設置了隊列解析的類.
HashServiceProvider
\Illuminate\Hashing\HashServiceProvider
也向IOC注冊一hash處理的類,並且是延遲加載的.
小結
查看Service Provicer
的使用后,我們可以發現,它們都做了同們的事,就是把相應的服務注入到ICO中,以供使用,從而構成完成的系統.所以,現在我們就可以明白Service Provider
是lararel完成模塊化設計的方法,只不過融入了一些laravel自己的特點.