Laravel 源码分析---Application 对 ServiceProvider 的管理与使用

标签: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 的功能,了解框架的设计思想,以及掌握框架的启动过程都具有很大的帮助。

你可能感兴趣的:(Laravel 源码分析---Application 对 ServiceProvider 的管理与使用)