Open musicode opened 9 years ago
在 app/config/app.php 中,配置了一个 providers 数组,这包含了所有已注册的 ServiceProvider。
初始化相关的代码有两行:
$providers = $config['providers']; $app->getProviderRepository()->load($app, $providers);
可见逻辑分两步:
ServiceProvider 是提供某类服务的对象,具体可参考 Laravel 文档。
我们分析一下 Illuminate\Log\LogServiceProvider,这个类比较简单,很适合拿来学习。
use Illuminate\Support\ServiceProvider; class LogServiceProvider extends ServiceProvider { }
所有的 ServiceProvider 都继承自 ServiceProvider 抽象类。这个类有两个属性:
ServiceProvider
比较重要的方法有以下这些:
除了 isDeferred 比较直白以外,其他方法需要更加详细的介绍。
前面提过,ProviderRepository 对象会调用 load 方法批量注册 ServiceProvider,其中核心步骤是 $app->register($provider),在 app 对象的 register 方法中,会调用 ServiceProvider 的 register 方法。
load
$app->register($provider)
register 是 ServiceProvider 类唯一的抽象方法,所以在子类中必须实现它,我们可以做一些初始化的操作,最常见的就是把组件注册到 IoC 容器。
抽象方法
Laravel 文档的建议如下:
如果您的应用程序有很多 IoC 绑定,或是想要分门别类,在不同文件组织绑定,您可以注册绑定在 服务提供者。 commands
如果您的应用程序有很多 IoC 绑定,或是想要分门别类,在不同文件组织绑定,您可以注册绑定在 服务提供者。
commands 是一个已实现的方法,可以通过它把 Artisan 命令注册到 Artisan console 实例中。
当 defer 属性为 true 时,provides 才有意义。
对于延迟加载的 ServiceProvider,app 对象可以通过 loadDeferredProvider 方法手动加载,这个方法需要一个 service 参数,即通过需要的服务名称加载对应的 ServiceProvider。
loadDeferredProvider
所以当我们描述一个 ServiceProvider 时,也需要给出它对外提供的服务名称,这个名称必须是全局唯一的,所以有类似命名空间的设计,如下:
public function provides() { return array('log', 'Psr\Log\LoggerInterface'); }
LogServiceProvider 采用的是以 \ 作为分隔符的形式,从整个框架来看,大部分采用的是以 . 作为分隔符的形式。
\
.
和 provides 一样,也是当 defer 属性为 true 时才有意义。
我们来看下 ProviderRepository 的 registerLoadEvents 方法,大概就知道 when 的作用了。
protected function registerLoadEvents(Application $app, $provider, array $events) { if (count($events) < 1) return; $app->make('events')->listen($events, function() use ($app, $provider) { $app->register($provider); }); }
也就是全局监听 when 事件数组,一旦触发事件就注册 ServiceProvider。
介绍 package 之前,先介绍一个辅助方法 guessPackagePath。
package 的 ServiceProvider 路径一般是 vender/vendorName/packageName/src/ServiceProvider.php,如果要猜 package 的路径,最简单的方式就是 ../..,guessPackagePath 正是这样实现的。
../..
package 用于注册包,比如包的配置,模板等,方法签名如下:
public function package($package, $namespace = null, $path = null)
常见的用法为 package('package/namespace'),这里分析一下 package 的执行步骤:
package('package/namespace')
其中第 2、3、5、6 步较难理解,我们会在 Config、Translator、View 章节介绍,这里就不展开了。
ProviderRepository 看起来像是 ServiceProvider 的容器,但实际上,它更多的是在维护一份配置。
通过上一节我们知道,每个 ServiceProvider 对象都有三种特性,如下:
所有已注册的 ServiceProvider 对象可在 app/config/app.php 找到,即使是核心组件,数量也超过了 10 个,如何管理这么多对象呢?
Laravel 为此设计了一份全量配置,格式如下:
{ "providers": [ ], "eager": [ ], "deferred": { }, "when": { } }
这份配置以 json 文件的格式保存在硬盘上(app/storage/meta/services.json),当应用初始化时,首先会检查硬盘是否有该文件,并且要求该文件的配置和 app/config/app.php 保持一致,如果不符合要求,会以 php 文件为准,更新 json 文件。
然后,根据 eager、deffered、when 的配置各自处理,比如监听 when 事件等,具体逻辑在介绍 ServiceProvider 时大致讲过,这里就不再赘述。
值得一提的是,ProviderRepository 并非是 ServiceProvider 的容器,说白了它只做两件事:
所谓 “容器” 的职责,最后还是交给了 IoC 容器,即 app 对象。
在 app/config/app.php 中,配置了一个 providers 数组,这包含了所有已注册的 ServiceProvider。
初始化相关的代码有两行:
可见逻辑分两步:
ServiceProvider
ServiceProvider 是提供某类服务的对象,具体可参考 Laravel 文档。
我们分析一下 Illuminate\Log\LogServiceProvider,这个类比较简单,很适合拿来学习。
所有的 ServiceProvider 都继承自
ServiceProvider
抽象类。这个类有两个属性:比较重要的方法有以下这些:
除了 isDeferred 比较直白以外,其他方法需要更加详细的介绍。
register
前面提过,ProviderRepository 对象会调用
load
方法批量注册 ServiceProvider,其中核心步骤是$app->register($provider)
,在 app 对象的 register 方法中,会调用 ServiceProvider 的 register 方法。register 是 ServiceProvider 类唯一的
抽象方法
,所以在子类中必须实现它,我们可以做一些初始化的操作,最常见的就是把组件注册到 IoC 容器。Laravel 文档的建议如下:
commands 是一个已实现的方法,可以通过它把 Artisan 命令注册到 Artisan console 实例中。
provides
当 defer 属性为 true 时,provides 才有意义。
对于延迟加载的 ServiceProvider,app 对象可以通过
loadDeferredProvider
方法手动加载,这个方法需要一个 service 参数,即通过需要的服务名称加载对应的 ServiceProvider。所以当我们描述一个 ServiceProvider 时,也需要给出它对外提供的服务名称,这个名称必须是全局唯一的,所以有类似命名空间的设计,如下:
LogServiceProvider 采用的是以
\
作为分隔符的形式,从整个框架来看,大部分采用的是以.
作为分隔符的形式。when
和 provides 一样,也是当 defer 属性为 true 时才有意义。
我们来看下 ProviderRepository 的 registerLoadEvents 方法,大概就知道 when 的作用了。
也就是全局监听 when 事件数组,一旦触发事件就注册 ServiceProvider。
package
介绍 package 之前,先介绍一个辅助方法 guessPackagePath。
package 的 ServiceProvider 路径一般是 vender/vendorName/packageName/src/ServiceProvider.php,如果要猜 package 的路径,最简单的方式就是
../..
,guessPackagePath 正是这样实现的。package 用于注册包,比如包的配置,模板等,方法签名如下:
常见的用法为
package('package/namespace')
,这里分析一下 package 的执行步骤:其中第 2、3、5、6 步较难理解,我们会在 Config、Translator、View 章节介绍,这里就不展开了。
ProviderRepository
ProviderRepository 看起来像是 ServiceProvider 的容器,但实际上,它更多的是在维护一份配置。
通过上一节我们知道,每个 ServiceProvider 对象都有三种特性,如下:
所有已注册的 ServiceProvider 对象可在 app/config/app.php 找到,即使是核心组件,数量也超过了 10 个,如何管理这么多对象呢?
Laravel 为此设计了一份全量配置,格式如下:
这份配置以 json 文件的格式保存在硬盘上(app/storage/meta/services.json),当应用初始化时,首先会检查硬盘是否有该文件,并且要求该文件的配置和 app/config/app.php 保持一致,如果不符合要求,会以 php 文件为准,更新 json 文件。
然后,根据 eager、deffered、when 的配置各自处理,比如监听 when 事件等,具体逻辑在介绍 ServiceProvider 时大致讲过,这里就不再赘述。
值得一提的是,ProviderRepository 并非是 ServiceProvider 的容器,说白了它只做两件事:
所谓 “容器” 的职责,最后还是交给了 IoC 容器,即 app 对象。