Hello,我是Rocket
这是我参与更文挑战的第5天,活动详情查看:更文挑战
- 服务提供者的功能是完成 Laravel 应用的引导启动,或者说是将 Laravel 中的各种服务「注册」到「Laravel 服务容器」,这样才能在后续处理 HTTP 请求时使用这些服务
- 如果说IOC容器是Laravel的房子,那么服务提供者就是支柱,就是基石
- 不了解IOC的同学,看我另外的文章传送门
1、概念
- 主要工作是使用「IOC容器」实现服务容器绑定、事件监听器、中间件,甚至是路由的注册
- 除核心服务外,几乎所有的服务提供者都定义在配置文件 config/app.php 文件中的 providers
- 基础的处理流程,Laravel 应用接收到 HTTP 请求时会去执行注册服务提供者,然后到了实际处理阶段,依据使用情况按需加载所需服务。
- 核心的服务提供者在
Illuminate\Foundation\Application
容器实例化就已经做了注册
<?php
...
class Application extends Container implements ApplicationContract, HttpKernelInterface
{
public function __construct($basePath = null)
{
...
$this->registerBaseServiceProviders();
...
}
/**
* Register all of the base service providers. 注册应用基础服务提供者
*
* @return void
*/
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));
$this->register(new LogServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
}
2、服务提供者的注册
2.1、通过引导程序注册服务提供者
服务提供者 注册 和 引导启动 直到处理 HTTP 请求阶段才开始。所以我们直接进入到 App\Console\Kernel 类,同时这个类继承于 Illuminate\Foundation\Http\Kernel 类。
class Kernel implements KernelContract
{
...
/**
* The bootstrap classes for the application. 应用引导类
*/
protected $bootstrappers = [
...
\Illuminate\Foundation\Bootstrap\RegisterProviders::class, // 用于注册(register)「服务提供者」的引导类
\Illuminate\Foundation\Bootstrap\BootProviders::class, // 用于启动(boot)「服务提供者」的引导类
];
public function handle($request)
{
try {
$request->enableHttpMethodParameterOverride();
$response = $this->sendRequestThroughRouter($request);
} catch (Exception $e) {
...
} catch (Throwable $e) {
...
}
...
}
protected function sendRequestThroughRouter($request)
{
...
// 1. 引导类引导启动。
$this->bootstrap();
// 2. 中间件及请求处理,生成响应并返回响应。
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}
/**
* Bootstrap the application for HTTP requests. 接收 HTTP 请求时启动应用引导程序。
*/
public function bootstrap()
{
// 引导类启动由 Application 容器引导启动。
if (! $this->app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers());
}
}
}
启动引导程序通过 $this->bootstrap ()
方法完成,其中包括所有服务提供者的注册和引导处理
进入Illuminate\Foundation\Application
容器中的 bootstrapWith() 方法,来看看容器是如何将引导类引导启动的
/**
* Run the given array of bootstrap classes. 执行给定引导程序
*/
public function bootstrapWith(array $bootstrappers)
{
$this->hasBeenBootstrapped = true;
foreach ($bootstrappers as $bootstrapper) {
$this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
// 从容器中解析出实例,然后调用实例的 bootstrap() 方法引导启动。
$this->make($bootstrapper)->bootstrap($this);
$this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
}
}
2.2 注册服务引导类的处理
追踪到Illuminate\Foundation\Bootstrap\RegisterProviders
实际调用的是 Illuminate\Foundation\Application
的 registerConfiguredProviders
public function registerConfiguredProviders()
{
(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
->load($providers->collapse()->toArray());
}
public function getCachedServicesPath()
{
return $this->bootstrapPath().'/cache/services.php';
}
2.3 由服务提供者仓库(ProviderRepository)执行服务提供者的注册处理
<?php
namespace Illuminate\Foundation;
class ProviderRepository
{
...
/**
* Register the application service providers. 注册应用的服务提供者。
*/
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']);
}
/**
* 将服务提供者编译到清单文件中缓存起来。
*/
protected function compileManifest($providers)
{
$manifest = $this->freshManifest($providers);
foreach ($providers as $provider) {
// 解析出 $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 registerLoadEvents($provider, array $events)
{
if (count($events) < 1) {
return;
}
$this->app->make('events')->listen($events, function () use ($provider) {
$this->app->register($provider);
});
}
2.4 缓存清单
- 缓存文件中 providers 放入了所有自定义和框架核心的服务。
- eager 数组中放入了所有需要立即启动的服务提供者。
- deferred 数组中放入了所有需要延迟加载的服务提供者。
- when 放入了延迟加载需要激活的事件。
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 (
),
...
),
2.5 Illuminate\Foundation\Application
容器完成注册
/**
* 在应用服务容器中注册一个服务提供者。
*/
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();
}
// 通过服务属性绑定
if (property_exists($provider, 'bindings')) {
foreach ($provider->bindings as $key => $value) {
$this->bind($key, $value);
}
}
//通过服务属性单例绑定
if (property_exists($provider, 'singletons')) {
foreach ($provider->singletons as $key => $value) {
$this->singleton($key, $value);
}
}
//注册到服务提供者数组以及已加载服务提供者数组
$this->markAsRegistered($provider);
// 判断 Laravel 应用是否已启动。已启动的话需要去执行启动处理。延迟服务提供者
if ($this->booted) {
$this->bootProvider($provider);
}
return $provider;
}
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']);
}
}
3、服务提供者的启动
服务容器的启动由类 \Illuminate\Foundation\Bootstrap\BootProviders
负责:
3.1 BootProviders 引导启动
class BootProviders
{
public function bootstrap(Application $app)
{
$app->boot();
}
}
3.2 由服务容器执行配置文件中的所有服务提供者服务完成启动
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);
});
//将启动状态置为true
$this->booted = true;
//执行启动后置回调
$this->fireAppCallbacks($this->bootedCallbacks);
}
protected function bootProvider(ServiceProvider $provider)
{
if (method_exists($provider, 'boot')) {
return $this->call([$provider, 'boot']);
}
}
}
3.3 延迟服务提供者的启动
对于延迟加载类型的服务提供者,我们要到使用时才会去执行它们内部的 register 和 boot 方法,也就是解析
Illuminate\Foundation\Application
的make
/**
* 从容器中解析出给定服务
*/
public function make($abstract, array $parameters = [])
{
$abstract = $this->getAlias($abstract);
// 判断这个接口是否为延迟类型的并且没有被解析过,是则去将它加载到容器中。
if (isset($this->deferredServices[$abstract]) && ! isset($this->instances[$abstract])) {
$this->loadDeferredProvider($abstract);
}
return parent::make($abstract, $parameters);
}
/**
* 加载给定延迟加载服务提供者
*/
public function loadDeferredProvider($service)
{
if (! isset($this->deferredServices[$service])) {
return;
}
$provider = $this->deferredServices[$service];
// 如果服务未注册则去注册并从延迟服务提供者集合中删除它。
if (! isset($this->loadedProviders[$provider])) {
$this->registerDeferredProvider($provider, $service);
}
}
/**
* 去执行服务提供者的注册方法。
*/
public function registerDeferredProvider($provider, $service = null)
{
if ($service) {
unset($this->deferredServices[$service]);
}
// 执行服务提供者注册服务 (如果应用已启动会执行boot)参考 2.5
$this->register($instance = new $provider($this));
// 执行服务提供者启动服务。
if (! $this->booted) {
//注册回调 (参考3.2)
$this->booting(function () use ($instance) {
$this->bootProvider($instance);
});
}
}
public function booting($callback)
{
$this->bootingCallbacks[] = $callback;
}
4、流程图
5、结尾
与诸君共勉之,希望对您有所帮助
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!