標(biāo)簽:laravel 源碼分析 Application ServiceProvider
我們?cè)谏弦黄恼轮锌戳?ServiceProvider 的基本源碼(見文章Laravel 源碼分析---ServiceProvider)。但是 Laravel 框架中 ServiceProvider 的實(shí)例化、注冊(cè)、啟動(dòng)、延遲加載等管理功能都是由框架的核心類 Application 來完成的。Application 是框架最核心的類,管理整個(gè)框架的啟動(dòng)、運(yùn)行以及整個(gè)生命周期,并通過 ServiceProvider 將其他功能的模塊載入框架。Application 是 Container 類的子類,所以也管理者框架中其他類的實(shí)例化、存儲(chǔ)等功能。今天我們來看一下 Application 類中和 ServiceProvider 管理相關(guān)的功能。
Application 管理功能概述
Application 對(duì) ServiceProvider 的管理主要有以下方面,首先根據(jù)配置文件獲得項(xiàng)目中用到的 ServiceProvider,解析分類,將相關(guān)數(shù)據(jù)存入 Application 的屬性進(jìn)行管理和使用,并緩存相關(guān)數(shù)據(jù)。根據(jù) ServiceProvider 是否延遲加載,進(jìn)行不同的操作。對(duì)于非延遲加載的 ServiceProvider,在 Application 解析 ServiceProvider 配置數(shù)據(jù)的時(shí)候就自動(dòng)注冊(cè)(運(yùn)行 register 方法),在 Application 啟動(dòng)的時(shí)候啟動(dòng) ServiceProvider(運(yùn)行 boot 方法)。對(duì)于延遲加載的 ServiceProvider ,在解析 ServiceProvider 配置的時(shí)候,監(jiān)聽觸發(fā)加載 ServiceProvider 的事件;并重載 Application 的 make 方法,如果 Application 要實(shí)例化的對(duì)象是延遲 ServiceProvider 提供的服務(wù)對(duì)象的話,先加載服務(wù)對(duì)象對(duì)應(yīng)的 ServiceProvider。
Application 管理 ServiceProvider 主要結(jié)構(gòu)
我們先來看一下 Application 管理 ServiceProvider 所使用的主要結(jié)構(gòu)
namespace Illuminate\Foundation;
class Application extends Container{
/**
* Indicates if the application has "booted".
* 標(biāo)識(shí) Application 是否啟動(dòng)
* @var bool
*/
protected $booted = false;
/**
* All of the registered service providers.
* 所有已經(jīng)注冊(cè)過的 ServiceProvider 的對(duì)象實(shí)例, 數(shù)字索引數(shù)組, 值為 ServiceProvider 的實(shí)例
* @var array
*/
protected $serviceProviders = [];
/**
* The names of the loaded service providers.
* 標(biāo)記加載過的 ServiceProvider, 鍵為ServiceProvider 的類名,值是一個(gè)布爾值。加載指 ServiceProvider 已經(jīng)實(shí)例化并且調(diào)用過 register 和 boot 方法
* @var array
*/
protected $loadedProviders = [];
/**
* The deferred services and their providers.
* 延遲加載服務(wù)的類名與注冊(cè)這個(gè)類的 ServiceProvider, 鍵為服務(wù)的類名,值為對(duì)應(yīng)的 ServiceProvider
* @var array
*/
protected $deferredServices = [];
/**
* Register all of the configured providers.
* 將所有的配置的 ServiceProvider 信息載入框架。注冊(cè)非延遲加載的 ServiceProvider,配置延遲加載的 ServiceProvider 與其載入的模塊對(duì)象的映射關(guān)系(配置 $this->deferredServices 變量),配置觸發(fā)加載延遲 ServiceProvider 的事件
* @return void
*/
public function registerConfiguredProviders(){}
/**
* Register a service provider with the application.
* 注冊(cè) $provider 配置的模塊到框架。實(shí)例化對(duì)應(yīng)的 ServiceProvider 類,并運(yùn)行 register 和 boot 方法
* @param \Illuminate\Support\ServiceProvider|string $provider
* @param array $options
* @param bool $force(是否忽略是否注冊(cè)過強(qiáng)制注冊(cè))
* @return \Illuminate\Support\ServiceProvider
*/
public function register($provider, $options = [], $force = false){}
/**
* Get the registered service provider instance if it exists.
* 返回 $provider 變量對(duì)應(yīng)的已經(jīng)注冊(cè)過的 ServiceProvider 實(shí)例
* @param \Illuminate\Support\ServiceProvider|string $provider
* @return \Illuminate\Support\ServiceProvider|null
*/
public function getProvider($provider){}
/**
* Resolve a service provider instance from the class name.
* 實(shí)例化 $provider 對(duì)應(yīng)的 ServiceProvider 實(shí)例
* @param string $provider
* @return \Illuminate\Support\ServiceProvider
*/
public function resolveProviderClass($provider){}
/**
* Mark the given provider as registered.
* 標(biāo)識(shí) $provider 對(duì)應(yīng)的 ServiceProvider 已經(jīng)注冊(cè)過
* @param \Illuminate\Support\ServiceProvider $provider
* @return void
*/
protected function markAsRegistered($provider){}
/**
* Load and boot all of the remaining deferred providers.
* 加載所有的延遲加載的 ServiceProvider
* @return void
*/
public function loadDeferredProviders(){}
/**
* Load the provider for a deferred service.
* 加載 $service 對(duì)應(yīng)的延遲加載的 ServiceProvider
* @param string $service
* @return void
*/
public function loadDeferredProvider($service){}
/**
* Register a deferred provider and service.
* 注冊(cè)并啟動(dòng)一個(gè)延遲加載的 ServiceProvider
* @param string $provider
* @param string $service
* @return void
*/
public function registerDeferredProvider($provider, $service = null){}
/**
* Resolve the given type from the container.
* 重載 Container 的 make 方法,使得 Application 可以根據(jù)要實(shí)例化服務(wù)的類名自動(dòng)加載其對(duì)應(yīng)的延遲加載的 ServiceProvider
* (Overriding Container::make)
*
* @param string $abstract
* @param array $parameters
* @return mixed
*/
public function make($abstract, array $parameters = []){}
/**
* Boot the application's service providers.
* 啟動(dòng)這個(gè) application 的 ServiceProvider
* @return void
*/
public function boot(){}
/**
* Boot the given service provider.
* 啟動(dòng) ServiceProvider。運(yùn)行 ServiceProvider 對(duì)象的 boot 方法
* @param \Illuminate\Support\ServiceProvider $provider
* @return mixed
*/
protected function bootProvider(ServiceProvider $provider){}
}
以上方法是 Application 管理 ServiceProvider 提供的具體屬性和方法,接下來我們來看具體功能的實(shí)現(xiàn)
ServiceProvider 配置的加載
我們先來看 ServiceProvider 配置的加載
class Application extends Container{
/**
* Register all of the configured providers.
* 將所有的配置的 ServiceProvider 信息載入框架
* @return void
*/
public function registerConfiguredProviders()
{
//返回緩存 manifest 數(shù)據(jù)的緩存文件路徑
$manifestPath = $this->getCachedServicesPath();
//導(dǎo)入框架配置的 ServiceProvider 信息,并解析到 Application 對(duì)象中。由 load 方法實(shí)現(xiàn)
(new ProviderRepository($this, new Filesystem, $manifestPath))
->load($this->config['app.providers']);
}
/**
* Get the path to the cached services.php file.
*
* @return string
*/
public function getCachedServicesPath()
{
return $this->bootstrapPath().'/cache/services.php';
}
/**
* Get the path to the bootstrap directory.
*
* @return string
*/
public function bootstrapPath()
{
return $this->basePath.DIRECTORY_SEPARATOR.'bootstrap';
}
}
通過以上代碼我們知道,框架會(huì)緩存 manifest 數(shù)據(jù),其包含了 框架解析過的 ServiceProvider 數(shù)據(jù)。我們先來看一下緩存的數(shù)據(jù)格式(位于 /bootstrap/cache/services.php 文件)。
return array (
'providers' =>
array (
0 => 'Illuminate\\Auth\\AuthServiceProvider',
1 => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
...
28 => 'App\\Providers\\ImageServiceProvider',
),
'eager' =>
array (
0 => 'Illuminate\\Auth\\AuthServiceProvider',
1 => 'Illuminate\\Cookie\\CookieServiceProvider',
...
15 => 'App\\Providers\\ImageServiceProvider',
),
'deferred' =>
array (
'Illuminate\\Broadcasting\\BroadcastManager' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
'Illuminate\\Contracts\\Broadcasting\\Factory' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
...
'command.ide-helper.models' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
),
'when' =>
array (
'Illuminate\\Broadcasting\\BroadcastServiceProvider' =>
array (
),
'Illuminate\\Bus\\BusServiceProvider' =>
array (
),
...
'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider' =>
array (
),
),
);
在上面數(shù)據(jù)中 providers 表示所有的 ServiceProvider 數(shù)組;eager 表示非延遲加載的 ServiceProvider 數(shù)組;deferred 表示延遲加載的服務(wù)類與其 ServiceProvider 的映射關(guān)系;when 表示觸發(fā)加載延遲加載 ServiceProvider 的事件數(shù)組。
接下來我們來看框架如何將 $providers 解析到 Application 里面,在 Illuminate\Foundation\ProviderRepository 類中的 load 方法實(shí)現(xiàn)
namespace Illuminate\Foundation;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
class ProviderRepository
{
/**
* Register the application service providers.
* 導(dǎo)入 $providers 的信息
* @param array $providers
* @return void
*/
public function load(array $providers)
{
//載入 manifest 的信息,即緩存文件里面的數(shù)據(jù)。如果緩存文件存在的話,加載緩存文件并將數(shù)據(jù)返回
$manifest = $this->loadManifest();
// 判斷是否需要重新編譯 $manifest,如果需要,根據(jù)編譯生成新的 $manifest 并緩存
if ($this->shouldRecompile($manifest, $providers)) {
$manifest = $this->compileManifest($providers);
}
//注冊(cè)觸發(fā)加載延遲加載的 $provider 的事件
foreach ($manifest['when'] as $provider => $events) {
$this->registerLoadEvents($provider, $events);
}
//注冊(cè)非延遲加載的$provider
foreach ($manifest['eager'] as $provider) {
$this->app->register($this->createProvider($provider));
}
//將延遲加載的 ServiceProvider 與其服務(wù)的對(duì)應(yīng)關(guān)系添加到 Application 對(duì)象
$this->app->addDeferredServices($manifest['deferred']);
}
/**
* Load the service provider manifest data.
* 載入緩存文件里面的 ServiceProvider 的 manifest 數(shù)據(jù)
* @return array|null
*/
public function loadManifest()
{
if ($this->files->exists($this->manifestPath)) {
$manifest = $this->files->getRequire($this->manifestPath);
if ($manifest) {
return array_merge(['when' => []], $manifest);
}
}
}
/**
* Determine if the manifest should be compiled.
* 根據(jù)緩存的 $manifest 數(shù)據(jù)和我們要加載的 $providers 判斷是否應(yīng)該重新編譯 $manifest 數(shù)據(jù)
* @param array $manifest
* @param array $providers
* @return bool
*/
public function shouldRecompile($manifest, $providers)
{
return is_null($manifest) || $manifest['providers'] != $providers;
}
/**
* Compile the application manifest file.
* 重新編譯 manifest 數(shù)據(jù)
* @param array $providers
* @return array
*/
protected function compileManifest($providers)
{
//根據(jù) $providers 創(chuàng)建新的 manifest 數(shù)據(jù)
$manifest = $this->freshManifest($providers);
foreach ($providers as $provider) {
$instance = $this->createProvider($provider);
//根據(jù) ServiceProvider 是否延遲加載,在 $manifest 里面進(jìn)行標(biāo)記
if ($instance->isDeferred()) {
foreach ($instance->provides() as $service) {
$manifest['deferred'][$service] = $provider;
}
$manifest['when'][$provider] = $instance->when();
}else {
$manifest['eager'][] = $provider;
}
}
//將編譯好的數(shù)據(jù)寫入緩存文件并返回
return $this->writeManifest($manifest);
}
/**
* Create a fresh service manifest data structure.
* 根據(jù) $providers 創(chuàng)建新的 manifest 數(shù)據(jù)
* @param array $providers
* @return array
*/
protected function freshManifest(array $providers)
{
return ['providers' => $providers, 'eager' => [], 'deferred' => []];
}
}
通過上面的代碼,我們將應(yīng)用配置的 ServiceProvider (app.providers)解析載入進(jìn) Application。
非延遲加載的 ServiceProvider 的管理
分析完 ServiceProvider 配置的加載和解析,我們接下來看 Application 對(duì)非延遲加載 ServiceProvider 的管理
namespace Illuminate\Foundation;
class Application extends Container{
/**
* Register a service provider with the application.
* 注冊(cè) ServiceProvider 配置的模塊到框架。實(shí)例化對(duì)應(yīng)的 ServiceProvider 類,并運(yùn)行 register 和 boot 方法
* @param \Illuminate\Support\ServiceProvider|string $provider
* @param array $options
* @param bool $force(是否忽略是否注冊(cè)過強(qiáng)制注冊(cè))
* @return \Illuminate\Support\ServiceProvider
*/
public function register($provider, $options = [], $force = false)
{
//如果 ServiceProvider 已經(jīng)注冊(cè)過,并且不需要強(qiáng)制重新注冊(cè),直接返回已經(jīng)注冊(cè)過的實(shí)例
if (($registered = $this->getProvider($provider)) && ! $force) {
return $registered;
}
//如果 $provider 是字符串,根據(jù)字符串實(shí)例化對(duì)應(yīng)的 ServiceProvider 對(duì)象
if (is_string($provider)) {
$provider = $this->resolveProviderClass($provider);
}
//調(diào)用 $provider 的 register 方法
if (method_exists($provider, 'register')) {
$provider->register();
}
...
//標(biāo)識(shí) $provider 已經(jīng)注冊(cè)過
$this->markAsRegistered($provider);
//如果 Application 已經(jīng)啟動(dòng),啟動(dòng)這個(gè) $provider
if ($this->booted) {
$this->bootProvider($provider);
}
return $provider;
}
/**
* Get the registered service provider instance if it exists.
* 返回 $provider 變量對(duì)應(yīng)的已經(jīng)注冊(cè)過的 ServiceProvider 實(shí)例
* @param \Illuminate\Support\ServiceProvider|string $provider
* @return \Illuminate\Support\ServiceProvider|null
*/
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;
});
}
/**
* Resolve a service provider instance from the class name.
* 實(shí)例化 $provider 對(duì)應(yīng)的 ServiceProvider 實(shí)例
* @param string $provider
* @return \Illuminate\Support\ServiceProvider
*/
public function resolveProviderClass($provider)
{
return new $provider($this);
}
/**
* Mark the given provider as registered.
* 標(biāo)識(shí) $provider 對(duì)應(yīng)的 ServiceProvider 已經(jīng)注冊(cè)過
* @param \Illuminate\Support\ServiceProvider $provider
* @return void
*/
protected function markAsRegistered($provider)
{
$this['events']->fire($class = get_class($provider), [$provider]);
$this->serviceProviders[] = $provider;
$this->loadedProviders[$class] = true;
}
/**
* Boot the given service provider.
* 啟動(dòng) $provider
* @param \Illuminate\Support\ServiceProvider $provider
* @return mixed
*/
protected function bootProvider(ServiceProvider $provider)
{
if (method_exists($provider, 'boot')) {
return $this->call([$provider, 'boot']);
}
}
/**
* Boot the application's service providers.
* 啟動(dòng)這個(gè) application 的 ServiceProvider
* @return void
*/
public function boot()
{
if ($this->booted) {
return;
}
//觸發(fā)啟動(dòng) Application 前的回調(diào)
$this->fireAppCallbacks($this->bootingCallbacks);
//array_walk函數(shù)對(duì)數(shù)組中的每個(gè)元素應(yīng)用用戶自定義函數(shù)。在自定義函數(shù)中,第一個(gè)參數(shù)是值,第二個(gè)參數(shù)是鍵。
array_walk($this->serviceProviders, function ($p) {
$this->bootProvider($p);
});
$this->booted = true;
//觸發(fā)啟動(dòng) Application 后的回調(diào)
$this->fireAppCallbacks($this->bootedCallbacks);
}
}
延遲加載的 ServiceProvider 管理
知道了 Application 如何對(duì)費(fèi)延遲的 ServiceProvider 進(jìn)行管理,我們來看 Application 對(duì)延遲加載的 ServiceProvider 的管理
namespace Illuminate\Foundation;
class Application extends Container{
/**
* Load and boot all of the remaining deferred providers.
* 加載所有的延遲加載的 ServiceProvider
* @return void
*/
public function loadDeferredProviders()
{
foreach ($this->deferredServices as $service => $provider) {
$this->loadDeferredProvider($service);
}
$this->deferredServices = [];
}
/**
* Load the provider for a deferred service.
* 加載 $service 對(duì)應(yīng)的 ServiceProvider
* @param string $service
* @return void
*/
public function loadDeferredProvider($service)
{
if (! isset($this->deferredServices[$service])) {
return;
}
//獲取 $service 對(duì)應(yīng)的延遲加載 $provider
$provider = $this->deferredServices[$service];
//如果 $provider 沒有加載,延遲加載 $provider
if (! isset($this->loadedProviders[$provider])) {
$this->registerDeferredProvider($provider, $service);
}
}
/**
* Register a deferred provider and service.
* 注冊(cè)并啟動(dòng)一個(gè)延遲加載的 $provider
* @param string $provider
* @param string $service
* @return void
*/
public function registerDeferredProvider($provider, $service = null)
{
//從延遲加載的服務(wù)中刪除 $service,因?yàn)槠鋵⒁患虞d注冊(cè)
if ($service) {
unset($this->deferredServices[$service]);
}
//注冊(cè) $provider。如果 Application 已經(jīng)啟動(dòng),同時(shí)調(diào)用 $provider 的 boot 方法
$this->register($instance = new $provider($this));
//如果 Application 沒有啟動(dòng),則添加一個(gè)啟動(dòng)事件,調(diào)用 $provider 的 boot 方法
if (! $this->booted) {
$this->booting(function () use ($instance) {
$this->bootProvider($instance);
});
}
}
/**
* Resolve the given type from the container.
* 重載 Container 的 make 方法,使得 Application 可以根據(jù)要實(shí)例化服務(wù)的類名自動(dòng)加載延遲加載的ServiceProvider
* (Overriding Container::make)
*
* @param string $abstract
* @param array $parameters
* @return mixed
*/
public function make($abstract, array $parameters = [])
{
$abstract = $this->getAlias($abstract);
//如果 $abstract 是延遲加載的 ServiceProvider 提供的對(duì)象,則載入 $abstract 對(duì)應(yīng)的 ServiceProvider
if (isset($this->deferredServices[$abstract])) {
$this->loadDeferredProvider($abstract);
}
return parent::make($abstract, $parameters);
}
}
總結(jié)
至此,我們已經(jīng)分析完了 Application 管理 ServiceProvider 相關(guān)功能的源碼。Application 對(duì) ServiceProvider 的管理是 Application 的重要功能之一,閱讀這部分的源碼對(duì)于我們理解 ServiceProvider 的功能,了解框架的設(shè)計(jì)思想,以及掌握框架的啟動(dòng)過程都具有很大的幫助。