【Laravel-海贼王系列】第八章, Provider 功能解析

栏目: PHP · 发布时间: 5年前

内容简介:我们直接从下面是内核引导启动函数接着就是把这一组类传给

本章将通过分析加载流程深入理解系统内置和 app 中配置的 provider 是如何加载的

我们直接从 Kernelhandle() 方法中开始分析, handle() 负责处理了请求,所有框架的启动也是在这里开始!

protected function sendRequestThroughRouter($request)
   {
       $this->app->instance('request', $request);

       Facade::clearResolvedInstance('request');

       $this->bootstrap();  // 这里就是内核的引导方法,故事也是从这里开始的。

       return (new Pipeline($this->app))
                   ->send($request)
                   ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                   ->then($this->dispatchToRouter());
   }
复制代码

下面是内核引导启动函数

$this->bootstrappers() = [
       \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
       \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
       \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
       \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
       \Illuminate\Foundation\Bootstrap\RegisterProviders::class, //找到了,它就是传给 App 对象进行启动的!
       \Illuminate\Foundation\Bootstrap\BootProviders::class,
]

public function bootstrap()
   {
       if (! $this->app->hasBeenBootstrapped()) {
           $this->app->bootstrapWith($this->bootstrappers());
       }
   }
复制代码

接着就是把这一组类传给 Application 对象的 bootstrapWith() 方法,继续下去

public function bootstrapWith(array $bootstrappers)
    {
        $this->hasBeenBootstrapped = true;

        foreach ($bootstrappers as $bootstrapper) {
            $this['events']->dispatch('bootstrapping: '.$bootstrapper, [$this]); // 启动中触发回调函数

            $this->make($bootstrapper)->bootstrap($this); // 这里是我们要关注的功能。

            $this['events']->dispatch('bootstrapped: '.$bootstrapper, [$this]);  // 启动完成后触发回调函数
        }
    }
复制代码

$this->make($bootstrapper)->bootstrap($this) 这个方法是从容器解析出传入的类,然后调用对应实例的 bootstrap($this) 方法

我们只追踪 \Illuminate\Foundation\Bootstrap\RegisterProviders::class 这个类,看看它的 bootstrap($this) 方法

public function bootstrap(Application $app)
    {
        $app->registerConfiguredProviders();
    }
复制代码

继续看 ApplicationregisterConfiguredProviders() 方法

public function registerConfiguredProviders()
    {
        $providers = Collection::make($this->config['app.providers'])
                        ->partition(function ($provider) {
                            return Str::startsWith($provider, 'Illuminate\\');
                        });

        $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]); 

        (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
                    ->load($providers->collapse()->toArray());
    }
复制代码

首先读取 $this->config['app.providers'] 中的所有类名,以下是 laravel 框架默认的加载的服务提供者

'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
        Illuminate\Bus\BusServiceProvider::class,
        Illuminate\Cache\CacheServiceProvider::class,
        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
        Illuminate\Cookie\CookieServiceProvider::class,
        Illuminate\Database\DatabaseServiceProvider::class,
        Illuminate\Encryption\EncryptionServiceProvider::class,
        Illuminate\Filesystem\FilesystemServiceProvider::class,
        Illuminate\Foundation\Providers\FoundationServiceProvider::class,
        Illuminate\Hashing\HashServiceProvider::class,
        Illuminate\Mail\MailServiceProvider::class,
        Illuminate\Notifications\NotificationServiceProvider::class,
        Illuminate\Pagination\PaginationServiceProvider::class,
        Illuminate\Pipeline\PipelineServiceProvider::class,
        Illuminate\Queue\QueueServiceProvider::class,
        Illuminate\Redis\RedisServiceProvider::class,
        Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
        Illuminate\Session\SessionServiceProvider::class,
        Illuminate\Translation\TranslationServiceProvider::class,
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,

        /*
         * Package Service Providers...
         */

        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,

    ],
复制代码

通过集合来操作,按照命名空间来分区,这里目前只有两组 IlluminateApp 开头的类

继续分析 $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);

[$this->make(PackageManifest::class)->providers()] 这段代码是 Laravel 5.5 引入的包自动发现功能,

主要实现了从所有的包的 composer.json 文件下读取 laravel 下的 providers 中的内容。

【Laravel-海贼王系列】第八章, Provider 功能解析

在引入这个功能后很多包都不需要我们手动再去 app.providers 中去配置了。

在这行代码执行完成之后 $providers 会变成一个分成了三段的集合对象。

【Laravel-海贼王系列】第八章, Provider 功能解析

继续看下去 (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath())) ->load($providers->collapse()->toArray());

public function __construct(ApplicationContract $app, Filesystem $files, $manifestPath)
    {
        $this->app = $app;
        $this->files = $files;
        $this->manifestPath = $manifestPath;
    }
复制代码

这里首先实例化了一个 ProviderRepository 对象,这个对象需要三个参数 ( $app , new Filesystem$this->getCachedServicesPath()

这里的 $this->getCachedServicesPath() 就是 bootstrap/cache/services.php 这个文件。

这里主要依靠 ProviderRepository 对象的 load() 来实现。

继续追踪 load() 方法

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']);
    }
复制代码

展开 $manifest = $this->loadManifest();

public function loadManifest()
    {
        if ($this->files->exists($this->manifestPath)) {
            $manifest = $this->files->getRequire($this->manifestPath);

            if ($manifest) {
                return array_merge(['when' => []], $manifest);
            }
        }
    }
复制代码

可以看到如果存在刚才传入的 bootstrap/cache/services.php 这个文件则直接加载,之后合并参数返回。 为了直观,我们来看看 $manifest 的样子

【Laravel-海贼王系列】第八章, Provider 功能解析

继续看下一段,如果传入的 $providers 和缓存中取出来的结果不相同,则通过 $providers 重新构建缓存

if ($this->shouldRecompile($manifest, $providers)) {
            $manifest = $this->compileManifest($providers);
        } 
复制代码

继续找下去,如果服务提供者的 when() 方法有返回事件则会在此处被监听

foreach ($manifest['when'] as $provider => $events) {
            $this->registerLoadEvents($provider, $events);
        }
复制代码

这里的类表示不需要延迟加载,因此框架会直接开始加载这些类

foreach ($manifest['eager'] as $provider) {
            $this->app->register($provider);
        }
复制代码

$this->app->addDeferredServices($manifest['deferred']); 最后一段就是延迟加载的功能了!

public function addDeferredServices(array $services)
    {
        $this->deferredServices = array_merge($this->deferredServices, $services);
    }
复制代码

这段代码就是将当前 $services 中的值合并到 $app 对象的 $deferredServices 成员中。

额外拓展,关于延迟加载的实现

这是 $app 对象的make方法,通过判断是否存在延迟加载的成员,如存在且没有在$instances中共享
的对象就会被加载,laravel 本身就是功能非常庞大的框架,因此延迟加载也是对性能提升的一种手段!

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);
    }
复制代码

分析到这里基本上 Laravel 的服务提供者功能是如何运作的就分解完了,其中关于 FilesystemProviderRepository

对象的功能没有详细分解,这里东西并不多,不展开了!

还有就是 PackageManifest::class 这个对象的功能主要是从 composer.json 的交互以及构建 bootstrap/cache 下面的缓存文件。

可以梳理一下思路:

框架是怎么加载的 (优先缓存文件,对比更新)

provider 提供了那些功能(延迟加载,事件监听)...

包自动发现的实现! (通过读取composer.json)

延迟加载的实现逻辑


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

如何构建敏捷项目管理团队

如何构建敏捷项目管理团队

丽萨·阿金斯 / 徐蓓蓓、白云峰、刘江华 / 电子工业出版社 / 2012-6 / 49.00元

《敏捷项目管理系列丛书•PMI-ACPSM考试指定教材•如何构建敏捷项目管理团队:ScrumMaster、敏捷教练与项目经理的实用指南》结合作者的亲身经历告诉读者如何建立一个高性能的敏捷项目管理团队,以及最终成为一名优秀的敏捷教练。作者将敏捷教练定义为导师、协助者、老师、问题解决者、冲突领航员、协作指挥者,正是这种不同角色之间的细微区别才使敏捷教练的工作富有深度。《敏捷项目管理系列丛书•PMI-A......一起来看看 《如何构建敏捷项目管理团队》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

html转js在线工具
html转js在线工具

html转js在线工具