内容简介:Laravel Providers——服务提供者的注册与启动源码解析
前言
本文 GitBook 地址: https://www.gitbook.com/book/leoyang90/laravel-source-analysis
服务提供者是 laravel
框架的重要组成部分,承载着各种服务,自定义的应用以及所有 Laravel
的核心服务都是通过服务提供者启动。本文将会介绍服务提供者的源码分析,关于服务提供者的使用,请参考官方文档 :服务提供者。
服务提供者的注册
服务提供者的启动由类 \Illuminate\Foundation\Bootstrap\RegisterProviders::class
负责,该类用于加载所有服务提供者的 register
函数,并保存延迟加载的服务的信息,以便实现延迟加载。
class RegisterProviders
{
public function bootstrap(Application $app)
{
$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']);
}
}
加载服务缓存文件
laravel
会把所有的服务整理起来,作为缓存写在缓存文件中:
return 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 (
),
...
),
- 缓存文件中
providers
放入了所有自定义和框架核心的服务。 eager
数组中放入了所有需要立即启动的服务提供者。deferred
数组中放入了所有需要延迟加载的服务提供者。when
放入了延迟加载需要激活的事件。
加载服务提供者缓存文件:
public function loadManifest()
{
if ($this->files->exists($this->manifestPath)) {
$manifest = $this->files->getRequire($this->manifestPath);
if ($manifest) {
return array_merge(['when' => []], $manifest);
}
}
}
编译服务提供者
若 laravel
中的服务提供者没有缓存文件或者有变动,那么就会重新生成缓存文件:
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);
}
protected function freshManifest(array $providers)
{
return ['providers' => $providers, 'eager' => [], 'deferred' => []];
}
- 如果服务提供者是需要立即注册的,那么将会放入缓存文件中
eager
数组中。 - 如果服务提供者是延迟加载的,那么其函数
provides()
通常会提供服务别名,这个服务别名通常是向服务容器中注册的别名,别名将会放入缓存文件的deferred
数组中。 - 延迟加载若有
event
事件激活,那么可以在when
函数中写入事件类,并写入缓存文件的when
数组中。
延迟服务提供者事件注册
延迟服务提供者除了利用 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
来调用:
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)
{
$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,
];
}
}
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);
});
}
}
关于服务提供者的注册函数:
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();
});
$this->app->alias(
BroadcastManager::class, BroadcastingFactory::class
);
}
public function provides()
{
return [
BroadcastManager::class,
BroadcastingFactory::class,
BroadcasterContract::class,
];
}
}
函数 register
为类 BroadcastingFactory
向 Ioc
容器绑定了特定的实现类 BroadcastManager
,这样 Ioc
容器中的 make
函数:
public function make($abstract)
{
$abstract = $this->getAlias($abstract);
if (isset($this->deferredServices[$abstract])) {
$this->loadDeferredProvider($abstract);
}
return parent::make($abstract);
}
parent::make($abstract)
就会正确的解析服务 BroadcastingFactory
。
因此函数 provides()
返回的元素一定都是 register()
向 IOC
容器中绑定的类名或者别名。这样当我们利用服务容器来利用 App::make()
解析这些类名的时候,服务容器才会根据服务提供者的 register()
函数中绑定的实现类,从而正确解析服务功能。
服务容器的启动
服务容器的启动由类 \Illuminate\Foundation\Bootstrap\BootProviders::class
负责:
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']);
}
}
}
以上所述就是小编给大家介绍的《Laravel Providers——服务提供者的注册与启动源码解析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Laravel初级教程之服务提供者
- Laravel创建服务器提供者实例
- [ Laravel 5.7 文档 ] 底层原理 —— 服务提供者
- 干货 | 研究以太坊 2.0 中的状态提供者模型
- 服务提供者(provider)与服务消费者(ribbon版本)-微服务架构
- 创新中关村报道称 Cocos 开发者沙龙为开发者、技术提供者等提供了交流合作平台
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
《电脑报》2005年合订本(上下)
电脑报社主编 / 西南师范大学出版社 / 2006-1 / 45.00元
全套上、下两册,浓缩2005年电脑报精华文章;附录包含70余篇简明IT应用指南,涵盖软件、硬件、数码、网络四大领域,配赠权威实用的2005-2006中国计算机年鉴光盘,近1.4GB海量信息与资源超值奉献,提供2005-2006全系列硬件、数码产品资讯,兼具知识性与资料性,连结购买每年《电脑报合订本》,你将拥有一套完整的实用大型电脑文库。一起来看看 《《电脑报》2005年合订本(上下)》 这本书的介绍吧!