标签:laravel 源码分析 Application ServiceProvider
我们在上一篇文章中看了 ServiceProvider 的基本源码(见文章Laravel 源码分析---ServiceProvider)。但是 Laravel 框架中 ServiceProvider 的实例化、注册、启动、延迟加载等管理功能都是由框架的核心类 Application 来完成的。Application 是框架最核心的类,管理整个框架的启动、运行以及整个生命周期,并通过 ServiceProvider 将其他功能的模块载入框架。Application 是 Container 类的子类,所以也管理者框架中其他类的实例化、存储等功能。今天我们来看一下 Application 类中和 ServiceProvider 管理相关的功能。
Application 管理功能概述
Application 对 ServiceProvider 的管理主要有以下方面,首先根据配置文件获得项目中用到的 ServiceProvider,解析分类,将相关数据存入 Application 的属性进行管理和使用,并缓存相关数据。根据 ServiceProvider 是否延迟加载,进行不同的操作。对于非延迟加载的 ServiceProvider,在 Application 解析 ServiceProvider 配置数据的时候就自动注册(运行 register 方法),在 Application 启动的时候启动 ServiceProvider(运行 boot 方法)。对于延迟加载的 ServiceProvider ,在解析 ServiceProvider 配置的时候,监听触发加载 ServiceProvider 的事件;并重载 Application 的 make 方法,如果 Application 要实例化的对象是延迟 ServiceProvider 提供的服务对象的话,先加载服务对象对应的 ServiceProvider。
Application 管理 ServiceProvider 主要结构
我们先来看一下 Application 管理 ServiceProvider 所使用的主要结构
namespace Illuminate\Foundation;
class Application extends Container{
/**
* Indicates if the application has "booted".
* 标识 Application 是否启动
* @var bool
*/
protected $booted = false;
/**
* All of the registered service providers.
* 所有已经注册过的 ServiceProvider 的对象实例, 数字索引数组, 值为 ServiceProvider 的实例
* @var array
*/
protected $serviceProviders = [];
/**
* The names of the loaded service providers.
* 标记加载过的 ServiceProvider, 键为ServiceProvider 的类名,值是一个布尔值。加载指 ServiceProvider 已经实例化并且调用过 register 和 boot 方法
* @var array
*/
protected $loadedProviders = [];
/**
* The deferred services and their providers.
* 延迟加载服务的类名与注册这个类的 ServiceProvider, 键为服务的类名,值为对应的 ServiceProvider
* @var array
*/
protected $deferredServices = [];
/**
* Register all of the configured providers.
* 将所有的配置的 ServiceProvider 信息载入框架。注册非延迟加载的 ServiceProvider,配置延迟加载的 ServiceProvider 与其载入的模块对象的映射关系(配置 $this->deferredServices 变量),配置触发加载延迟 ServiceProvider 的事件
* @return void
*/
public function registerConfiguredProviders(){}
/**
* Register a service provider with the application.
* 注册 $provider 配置的模块到框架。实例化对应的 ServiceProvider 类,并运行 register 和 boot 方法
* @param \Illuminate\Support\ServiceProvider|string $provider
* @param array $options
* @param bool $force(是否忽略是否注册过强制注册)
* @return \Illuminate\Support\ServiceProvider
*/
public function register($provider, $options = [], $force = false){}
/**
* Get the registered service provider instance if it exists.
* 返回 $provider 变量对应的已经注册过的 ServiceProvider 实例
* @param \Illuminate\Support\ServiceProvider|string $provider
* @return \Illuminate\Support\ServiceProvider|null
*/
public function getProvider($provider){}
/**
* Resolve a service provider instance from the class name.
* 实例化 $provider 对应的 ServiceProvider 实例
* @param string $provider
* @return \Illuminate\Support\ServiceProvider
*/
public function resolveProviderClass($provider){}
/**
* Mark the given provider as registered.
* 标识 $provider 对应的 ServiceProvider 已经注册过
* @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 对应的延迟加载的 ServiceProvider
* @param string $service
* @return void
*/
public function loadDeferredProvider($service){}
/**
* Register a deferred provider and service.
* 注册并启动一个延迟加载的 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 可以根据要实例化服务的类名自动加载其对应的延迟加载的 ServiceProvider
* (Overriding Container::make)
*
* @param string $abstract
* @param array $parameters
* @return mixed
*/
public function make($abstract, array $parameters = []){}
/**
* Boot the application's service providers.
* 启动这个 application 的 ServiceProvider
* @return void
*/
public function boot(){}
/**
* Boot the given service provider.
* 启动 ServiceProvider。运行 ServiceProvider 对象的 boot 方法
* @param \Illuminate\Support\ServiceProvider $provider
* @return mixed
*/
protected function bootProvider(ServiceProvider $provider){}
}
以上方法是 Application 管理 ServiceProvider 提供的具体属性和方法,接下来我们来看具体功能的实现
ServiceProvider 配置的加载
我们先来看 ServiceProvider 配置的加载
class Application extends Container{
/**
* Register all of the configured providers.
* 将所有的配置的 ServiceProvider 信息载入框架
* @return void
*/
public function registerConfiguredProviders()
{
//返回缓存 manifest 数据的缓存文件路径
$manifestPath = $this->getCachedServicesPath();
//导入框架配置的 ServiceProvider 信息,并解析到 Application 对象中。由 load 方法实现
(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';
}
}
通过以上代码我们知道,框架会缓存 manifest 数据,其包含了 框架解析过的 ServiceProvider 数据。我们先来看一下缓存的数据格式(位于 /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 (
),
),
);
在上面数据中 providers 表示所有的 ServiceProvider 数组;eager 表示非延迟加载的 ServiceProvider 数组;deferred 表示延迟加载的服务类与其 ServiceProvider 的映射关系;when 表示触发加载延迟加载 ServiceProvider 的事件数组。
接下来我们来看框架如何将 $providers 解析到 Application 里面,在 Illuminate\Foundation\ProviderRepository
类中的 load
方法实现
namespace Illuminate\Foundation;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
class ProviderRepository
{
/**
* Register the application service providers.
* 导入 $providers 的信息
* @param array $providers
* @return void
*/
public function load(array $providers)
{
//载入 manifest 的信息,即缓存文件里面的数据。如果缓存文件存在的话,加载缓存文件并将数据返回
$manifest = $this->loadManifest();
// 判断是否需要重新编译 $manifest,如果需要,根据编译生成新的 $manifest 并缓存
if ($this->shouldRecompile($manifest, $providers)) {
$manifest = $this->compileManifest($providers);
}
//注册触发加载延迟加载的 $provider 的事件
foreach ($manifest['when'] as $provider => $events) {
$this->registerLoadEvents($provider, $events);
}
//注册非延迟加载的$provider
foreach ($manifest['eager'] as $provider) {
$this->app->register($this->createProvider($provider));
}
//将延迟加载的 ServiceProvider 与其服务的对应关系添加到 Application 对象
$this->app->addDeferredServices($manifest['deferred']);
}
/**
* Load the service provider manifest data.
* 载入缓存文件里面的 ServiceProvider 的 manifest 数据
* @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.
* 根据缓存的 $manifest 数据和我们要加载的 $providers 判断是否应该重新编译 $manifest 数据
* @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 数据
* @param array $providers
* @return array
*/
protected function compileManifest($providers)
{
//根据 $providers 创建新的 manifest 数据
$manifest = $this->freshManifest($providers);
foreach ($providers as $provider) {
$instance = $this->createProvider($provider);
//根据 ServiceProvider 是否延迟加载,在 $manifest 里面进行标记
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);
}
/**
* Create a fresh service manifest data structure.
* 根据 $providers 创建新的 manifest 数据
* @param array $providers
* @return array
*/
protected function freshManifest(array $providers)
{
return ['providers' => $providers, 'eager' => [], 'deferred' => []];
}
}
通过上面的代码,我们将应用配置的 ServiceProvider (app.providers)解析载入进 Application。
非延迟加载的 ServiceProvider 的管理
分析完 ServiceProvider 配置的加载和解析,我们接下来看 Application 对非延迟加载 ServiceProvider 的管理
namespace Illuminate\Foundation;
class Application extends Container{
/**
* Register a service provider with the application.
* 注册 ServiceProvider 配置的模块到框架。实例化对应的 ServiceProvider 类,并运行 register 和 boot 方法
* @param \Illuminate\Support\ServiceProvider|string $provider
* @param array $options
* @param bool $force(是否忽略是否注册过强制注册)
* @return \Illuminate\Support\ServiceProvider
*/
public function register($provider, $options = [], $force = false)
{
//如果 ServiceProvider 已经注册过,并且不需要强制重新注册,直接返回已经注册过的实例
if (($registered = $this->getProvider($provider)) && ! $force) {
return $registered;
}
//如果 $provider 是字符串,根据字符串实例化对应的 ServiceProvider 对象
if (is_string($provider)) {
$provider = $this->resolveProviderClass($provider);
}
//调用 $provider 的 register 方法
if (method_exists($provider, 'register')) {
$provider->register();
}
...
//标识 $provider 已经注册过
$this->markAsRegistered($provider);
//如果 Application 已经启动,启动这个 $provider
if ($this->booted) {
$this->bootProvider($provider);
}
return $provider;
}
/**
* Get the registered service provider instance if it exists.
* 返回 $provider 变量对应的已经注册过的 ServiceProvider 实例
* @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.
* 实例化 $provider 对应的 ServiceProvider 实例
* @param string $provider
* @return \Illuminate\Support\ServiceProvider
*/
public function resolveProviderClass($provider)
{
return new $provider($this);
}
/**
* Mark the given provider as registered.
* 标识 $provider 对应的 ServiceProvider 已经注册过
* @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.
* 启动 $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.
* 启动这个 application 的 ServiceProvider
* @return void
*/
public function boot()
{
if ($this->booted) {
return;
}
//触发启动 Application 前的回调
$this->fireAppCallbacks($this->bootingCallbacks);
//array_walk函数对数组中的每个元素应用用户自定义函数。在自定义函数中,第一个参数是值,第二个参数是键。
array_walk($this->serviceProviders, function ($p) {
$this->bootProvider($p);
});
$this->booted = true;
//触发启动 Application 后的回调
$this->fireAppCallbacks($this->bootedCallbacks);
}
}
延迟加载的 ServiceProvider 管理
知道了 Application 如何对费延迟的 ServiceProvider 进行管理,我们来看 Application 对延迟加载的 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 对应的 ServiceProvider
* @param string $service
* @return void
*/
public function loadDeferredProvider($service)
{
if (! isset($this->deferredServices[$service])) {
return;
}
//获取 $service 对应的延迟加载 $provider
$provider = $this->deferredServices[$service];
//如果 $provider 没有加载,延迟加载 $provider
if (! isset($this->loadedProviders[$provider])) {
$this->registerDeferredProvider($provider, $service);
}
}
/**
* Register a deferred provider and service.
* 注册并启动一个延迟加载的 $provider
* @param string $provider
* @param string $service
* @return void
*/
public function registerDeferredProvider($provider, $service = null)
{
//从延迟加载的服务中删除 $service,因为其将要被加载注册
if ($service) {
unset($this->deferredServices[$service]);
}
//注册 $provider。如果 Application 已经启动,同时调用 $provider 的 boot 方法
$this->register($instance = new $provider($this));
//如果 Application 没有启动,则添加一个启动事件,调用 $provider 的 boot 方法
if (! $this->booted) {
$this->booting(function () use ($instance) {
$this->bootProvider($instance);
});
}
}
/**
* Resolve the given type from the container.
* 重载 Container 的 make 方法,使得 Application 可以根据要实例化服务的类名自动加载延迟加载的ServiceProvider
* (Overriding Container::make)
*
* @param string $abstract
* @param array $parameters
* @return mixed
*/
public function make($abstract, array $parameters = [])
{
$abstract = $this->getAlias($abstract);
//如果 $abstract 是延迟加载的 ServiceProvider 提供的对象,则载入 $abstract 对应的 ServiceProvider
if (isset($this->deferredServices[$abstract])) {
$this->loadDeferredProvider($abstract);
}
return parent::make($abstract, $parameters);
}
}
总结
至此,我们已经分析完了 Application 管理 ServiceProvider 相关功能的源码。Application 对 ServiceProvider 的管理是 Application 的重要功能之一,阅读这部分的源码对于我们理解 ServiceProvider 的功能,了解框架的设计思想,以及掌握框架的启动过程都具有很大的帮助。