服務提供器是所有 Laravel html' target='_blank'>應用程序引導中心。你的應用程序自定義的服務、第三方資源包提供的服務以及 Laravel 的所有核心服務都是通過服務提供器進行注冊(register)和引導(boot)的。
拿一個Laravel框架自帶的服務提供器來舉例子
class BroadcastServiceProvider extends ServiceProvider protected $defer = true; public function register() $this- app- singleton(BroadcastManager::class, function ($app) { return new BroadcastManager($app); $this- app- singleton(BroadcasterContract::class, function ($app) { return $app- make(BroadcastManager::class)- connection(); //將BroadcastingFactory::class設置為BroadcastManager::class的別名 $this- app- alias( BroadcastManager::class, BroadcastingFactory::class public function provides() return [ BroadcastManager::class, BroadcastingFactory::class, BroadcasterContract::class,}
在服務提供器BroadcastServiceProvider的register中, 為BroadcastingFactory的類名綁定了類實現BroadcastManager,這樣就能通過服務容器來make出通過BroadcastingFactory::class綁定的服務BroadcastManger對象供應用程序使用了。
本文主要時來梳理一下laravel是如何注冊、和初始化這些服務的,關于如何編寫自己的服務提供器,可以參考官方文檔
BootStrap首先laravel注冊和引導應用需要的服務是發生在尋找路由處理客戶端請求之前的Bootstrap階段的,在框架的入口文件里我們可以看到,框架在實例化了Application對象后從服務容器中解析出了HTTP Kernel對象
$kernel = $app- make(Illuminate/Contracts/Http/Kernel::class);$response = $kernel- handle( $request = Illuminate/Http/Request::capture());
在Kernel處理請求時會先讓請求通過中間件然后在發送請求給路由對應的控制器方法, 在這之前有一個BootStrap階段來引導啟動Laravel應用程序,如下面代碼所示。
public function handle($request) ...... $response = $this- sendRequestThroughRouter($request); ...... return $response;}
protected function sendRequestThroughRouter($request) $this- app- instance( request , $request); Facade::clearResolvedInstance( request $this- bootstrap(); return (new Pipeline($this- app)) - send($request) - through($this- app- shouldSkipMiddleware() ? [] : $this- middleware) - then($this- dispatchToRouter());//引導啟動Laravel應用程序public function bootstrap() if (! $this- app- hasBeenBootstrapped()) { /**依次執行$bootstrappers中每一個bootstrapper的bootstrap()函數 $bootstrappers = [ Illuminate/Foundation/Bootstrap/DetectEnvironment , Illuminate/Foundation/Bootstrap/LoadConfiguration , Illuminate/Foundation/Bootstrap/ConfigureLogging , Illuminate/Foundation/Bootstrap/HandleExceptions , Illuminate/Foundation/Bootstrap/RegisterFacades , Illuminate/Foundation/Bootstrap/RegisterProviders , Illuminate/Foundation/Bootstrap/BootProviders , ];*/ $this- app- bootstrapWith($this- bootstrappers()); }
上面bootstrap中會分別執行每一個bootstrapper的bootstrap方法來引導啟動應用程序的各個部分
1. DetectEnvironment 檢查環境 2. LoadConfiguration 加載應用配置 3. ConfigureLogging 配置日至 4. HandleException 注冊異常處理的Handler 5. RegisterFacades 注冊Facades 6. RegisterProviders 注冊Providers 7. BootProviders 啟動Providers
啟動應用程序的最后兩部就是注冊服務提供這和啟動提供者,如果對前面幾個階段具體時怎么實現的可以參考這篇文章。在這里我們主要關注服務提供器的注冊和啟動。
先來看注冊服務提供器,服務提供器的注冊由類 /Illuminate/Foundation/Bootstrap/RegisterProviders::class 負責,該類用于加載所有服務提供器的 register 函數,并保存延遲加載的服務的信息,以便實現延遲加載。
class RegisterProviders public function bootstrap(Application $app) //調用了Application的registerConfiguredProviders() $app- registerConfiguredProviders();class Application extends Container implements ApplicationContract, HttpKernelInterface public function registerConfiguredProviders() (new ProviderRepository($this, new Filesystem, $this- getCachedServicesPath())) - load($this- config[ app.providers public function getCachedServicesPath() return $this- bootstrapPath(). /cache/services.php }
可以看出,所有服務提供器都在配置文件 app.php 文件的 providers 數組中。類 ProviderRepository 負責所有的服務加載功能:
class ProviderRepository public function load(array $providers) $manifest = $this- loadManifest(); if ($this- shouldRecompile($manifest, $providers)) { $manifest = $this- compileManifest($providers); foreach ($manifest[ when ] as $provider = $events) { $this- registerLoadEvents($provider, $events); foreach ($manifest[ eager ] as $provider) { $this- app- register($provider); $this- app- addDeferredServices($manifest[ deferred }
loadManifest()會加載服務提供器緩存文件services.php,如果框架是第一次啟動時沒有這個文件的,或者是緩存文件中的providers數組項與config/app.php里的providers數組項不一致都會編譯生成services.php。
//判斷是否需要編譯生成services文件public function shouldRecompile($manifest, $providers) return is_null($manifest) || $manifest[ providers ] != $providers;//編譯生成文件的具體過程protected function compileManifest($providers) $manifest = $this- freshManifest($providers); foreach ($providers as $provider) { $instance = $this- createProvider($provider); if ($instance- isDeferred()) { foreach ($instance- provides() as $service) { $manifest[ deferred ][$service] = $provider; $manifest[ when ][$provider] = $instance- when(); else { $manifest[ eager ][] = $provider; return $this- writeManifest($manifest);
return [ providers = $providers, eager = [], deferred = []];}
緩存文件中 providers 放入了所有自定義和框架核心的服務。
如果服務提供器是需要立即注冊的,那么將會放入緩存文件中 eager 數組中。
如果服務提供器是延遲加載的,那么其函數 provides() 通常會提供服務別名,這個服務別名通常是向服務容器中注冊的別名,別名將會放入緩存文件的 deferred 數組中,與真正要注冊的服務提供器組成一個鍵值對。
延遲加載若由 event 事件激活,那么可以在 when 函數中寫入事件類,并寫入緩存文件的 when 數組中。
生成的緩存文件內容如下:
array ( providers = array ( 0 = Illuminate//Auth//AuthServiceProvider , 1 = Illuminate//Broadcasting//BroadcastServiceProvider , eager = array ( 0 = Illuminate//Auth//AuthServiceProvider , 1 = Illuminate//Cookie//CookieServiceProvider , deferred = array ( Illuminate//Broadcasting//BroadcastManager = Illuminate//Broadcasting//BroadcastServiceProvider , Illuminate//Contracts//Broadcasting//Factory = Illuminate//Broadcasting//BroadcastServiceProvider , when = array ( Illuminate//Broadcasting//BroadcastServiceProvider = array ()事件觸發時注冊延遲服務提供器
延遲服務提供器除了利用 IOC 容器解析服務方式激活,還可以利用 Event 事件來激活:
protected function registerLoadEvents($provider, array $events) if (count($events) 1) { return; $this- app- make( events )- listen($events, function () use ($provider) { $this- app- register($provider);}即時注冊服務提供器
需要即時注冊的服務提供器的register方法由Application的register方法里來調用:
class Application extends Container implements ApplicationContract, HttpKernelInterface public function register($provider, $options = [], $force = false) if (($registered = $this- getProvider($provider)) ! $force) { return $registered; if (is_string($provider)) { $provider = $this- resolveProvider($provider); if (method_exists($provider, register )) { $provider- register(); $this- markAsRegistered($provider); if ($this- booted) { $this- bootProvider($provider); return $provider; public function getProvider($provider) $name = is_string($provider) ? $provider : get_class($provider); return Arr::first($this- serviceProviders, function ($value) use ($name) { return $value instanceof $name; public function resolveProvider($provider) return new $provider($this); protected function markAsRegistered($provider) //這個屬性在稍后booting服務時會用到 $this- serviceProviders[] = $provider; $this- loadedProviders[get_class($provider)] = true; protected function bootProvider(ServiceProvider $provider) if (method_exists($provider, boot )) { return $this- call([$provider, boot }
可以看出,服務提供器的注冊過程:
判斷當前服務提供器是否被注冊過,如注冊過直接返回對象
解析服務提供器
調用服務提供器的 register 函數
標記當前服務提供器已經注冊完畢
若框架已經加載注冊完畢所有的服務容器,那么就啟動服務提供器的 boot 函數,該函數由于是 call 調用,所以支持依賴注入。
服務解析時注冊延遲服務提供器延遲服務提供器首先需要添加到 Application 中
public function addDeferredServices(array $services) $this- deferredServices = array_merge($this- deferredServices, $services);}
我們之前說過,延遲服務提供器的激活注冊有兩種方法:事件與服務解析。
當特定的事件被激發后,就會調用 Application 的 register 函數,進而調用服務提供器的 register 函數,實現服務的注冊。
當利用 Ioc 容器解析服務名時,例如解析服務名 BroadcastingFactory:
class BroadcastServiceProvider extends ServiceProvider protected $defer = true; public function provides() return [ BroadcastManager::class, BroadcastingFactory::class, BroadcasterContract::class,}
在Application的make方法里會通過別名BroadcastingFactory查找是否有對應的延遲注冊的服務提供器,如果有的話那么
就先通過registerDeferredProvider方法注冊服務提供器。
class Application extends Container implements ApplicationContract, HttpKernelInterface public function make($abstract) $abstract = $this- getAlias($abstract); if (isset($this- deferredServices[$abstract])) { $this- loadDeferredProvider($abstract); return parent::make($abstract); public function loadDeferredProvider($service) if (! isset($this- deferredServices[$service])) { return; $provider = $this- deferredServices[$service]; if (! isset($this- loadedProviders[$provider])) { $this- registerDeferredProvider($provider, $service);}
由 deferredServices 數組可以得知,BroadcastingFactory 為延遲服務,接著程序會利用函數 loadDeferredProvider 來加載延遲服務提供器,調用服務提供器的 register 函數,若當前的框架還未注冊完全部服務。那么將會放入服務啟動的回調函數中,以待服務啟動時調用:
public function registerDeferredProvider($provider, $service = null) if ($service) { unset($this- deferredServices[$service]); $this- register($instance = new $provider($this)); if (! $this- booted) { $this- booting(function () use ($instance) { $this- bootProvider($instance);}
還是拿服務提供器BroadcastServiceProvider來舉例:
class BroadcastServiceProvider extends ServiceProvider protected $defer = true; public function register() $this- app- singleton(BroadcastManager::class, function ($app) { return new BroadcastManager($app); $this- app- singleton(BroadcasterContract::class, function ($app) { return $app- make(BroadcastManager::class)- connection(); //將BroadcastingFactory::class設置為BroadcastManager::class的別名 $this- app- alias( BroadcastManager::class, BroadcastingFactory::class public function provides() return [ BroadcastManager::class, BroadcastingFactory::class, BroadcasterContract::class,}
函數 register 為類 BroadcastingFactory 向 服務容器綁定了特定的實現類 BroadcastManager,Application中的 make 函數里執行parent::make($abstract) 通過服務容器的make就會正確的解析出服務 BroadcastingFactory。
因此函數 provides() 返回的元素一定都是 register() 向 服務容器中綁定的類名或者別名。這樣當我們利用App::make() 解析這些類名的時候,服務容器才會根據服務提供器的 register() 函數中綁定的實現類,正確解析出服務功能。
啟動ApplicationApplication的啟動由類 /Illuminate/Foundation/Bootstrap/BootProviders 負責:
class BootProviders public function bootstrap(Application $app) $app- boot();class Application extends Container implements ApplicationContract, HttpKernelInterface public function boot() if ($this- booted) { return; $this- fireAppCallbacks($this- bootingCallbacks); array_walk($this- serviceProviders, function ($p) { $this- bootProvider($p); $this- booted = true; $this- fireAppCallbacks($this- bootedCallbacks); protected function bootProvider(ServiceProvider $provider) if (method_exists($provider, boot )) { return $this- call([$provider, boot }
引導應用Application的serviceProviders屬性中記錄的所有服務提供器,就是依次調用這些服務提供器的boot方法,引導完成后$this- booted = true 就代表應用Application正式啟動了,可以開始處理請求了。這里額外說一句,之所以等到所有服務提供器都注冊完后再來進行引導是因為有可能在一個服務提供器的boot方法里調用了其他服務提供器注冊的服務,所以需要等到所有即時注冊的服務提供器都register完成后再來boot。
以上就是本文的全部內容,希望對大家的學習有所幫助,更多相關內容請關注PHP !
相關推薦:
Laravel控制器的解讀
Laravel核心解讀Response
以上就是Laravel服務提供器(ServiceProvider)的解讀的詳細內容,PHP教程
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。
新聞熱點
疑難解答