zoukankan      html  css  js  c++  java
  • laravel5.5源码笔记(二、服务提供者provider)

    laravel里所谓的provider服务提供者,其实是对某一类功能进行整合,与做一些使用前的初始化引导工作。laravel里的服务提供者也分为,系统核心服务提供者、与一般系统服务提供者。例如上一篇博文里介绍的,最早在application中进行注册的event、log、routing这些就是系统的核心服务,laravel的初始化需要他们。那么现在就先来看一下provider的运行流程。

    1     protected function registerBaseServiceProviders()
    2     {
    3         $this->register(new EventServiceProvider($this));
    4 
    5         $this->register(new LogServiceProvider($this));
    6 
    7         $this->register(new RoutingServiceProvider($this));
    8     }

    其他的serviceProvider则是指config/app.php中providers数组所配置的provider了,基本都是些laravel系统提供的工具型provider

     1     'providers' => [
     2 
     3         /*
     4          * Laravel Framework Service Providers...
     5          */
     6         IlluminateAuthAuthServiceProvider::class,
     7         IlluminateBroadcastingBroadcastServiceProvider::class,
     8         IlluminateBusBusServiceProvider::class,
     9         IlluminateCacheCacheServiceProvider::class,
    10         IlluminateFoundationProvidersConsoleSupportServiceProvider::class,
    11         IlluminateCookieCookieServiceProvider::class,
    12         IlluminateDatabaseDatabaseServiceProvider::class,
    13         IlluminateEncryptionEncryptionServiceProvider::class,
    14         IlluminateFilesystemFilesystemServiceProvider::class,
    15         IlluminateFoundationProvidersFoundationServiceProvider::class,
    16         IlluminateHashingHashServiceProvider::class,
    17         IlluminateMailMailServiceProvider::class,
    18         IlluminateNotificationsNotificationServiceProvider::class,
    19         IlluminatePaginationPaginationServiceProvider::class,
    20         IlluminatePipelinePipelineServiceProvider::class,
    21         IlluminateQueueQueueServiceProvider::class,
    22         IlluminateRedisRedisServiceProvider::class,
    23         IlluminateAuthPasswordsPasswordResetServiceProvider::class,
    24         IlluminateSessionSessionServiceProvider::class,
    25         IlluminateTranslationTranslationServiceProvider::class,
    26         IlluminateValidationValidationServiceProvider::class,
    27         IlluminateViewViewServiceProvider::class,
    28         //MaatwebsiteExcelExcelServiceProvider::class,  这个是我自己测试的时候加的
    29 
    30         /*
    31          * Package Service Providers...
    32          */
    33 
    34         /*
    35          * Application Service Providers...
    36          */
    37         AppProvidersAppServiceProvider::class,
    38         AppProvidersAuthServiceProvider::class,
    39         // AppProvidersBroadcastServiceProvider::class,
    40         AppProvidersEventServiceProvider::class,
    41         AppProvidersRouteServiceProvider::class,
    42 
    43     ],

    那么这些配置中的provider会在什么时候加载呢?上一篇博文中介绍的当$kernel对象通过handle方法传入request时,会执行sendRequestThroughRouter方法,这个方法中的bootstrap方法会加载laravel系统初始化所需的对象并运行,其中RegisterProviders类便是用来注册刚刚config文件内所记录的provider的

     1     public function bootstrap()
     2     {
     3         if (! $this->app->hasBeenBootstrapped()) {
     4             $this->app->bootstrapWith($this->bootstrappers());
     5         }
     6     }
     7 
     8     protected $bootstrappers = [
     9         IlluminateFoundationBootstrapLoadEnvironmentVariables::class,
    10         IlluminateFoundationBootstrapLoadConfiguration::class,
    11         IlluminateFoundationBootstrapHandleExceptions::class,
    12         //注册facade门面类
    13         IlluminateFoundationBootstrapRegisterFacades::class,
    14         //注册provider
    15         IlluminateFoundationBootstrapRegisterProviders::class,
    16         //引导provider执行其中boot方法内的代码
    17         IlluminateFoundationBootstrapBootProviders::class,
    18     ];    

    这几个文件的内容都很简单,并且都是调用了application中的方法

     1     public function bootstrapWith(array $bootstrappers)
     2     {
     3         $this->hasBeenBootstrapped = true;
     4 
     5         foreach ($bootstrappers as $bootstrapper) {
     6             $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
     7             //make了刚刚传入的$bootstrappers数组,并执行了其中的bootstrap方法,暂且只看provider
     8             $this->make($bootstrapper)->bootstrap($this);
     9 
    10             $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
    11         }
    12     }
    13     
    14     //IlluminateFoundationBootstrapRegisterProviders.php
    15     public function bootstrap(Application $app)
    16     {
    17         $app->registerConfiguredProviders();
    18     }
    19 
    20     //IlluminateFoundationBootstrapBootProviders.php
    21     public function bootstrap(Application $app)
    22     {
    23         $app->boot();
    24     }

    这里绕了一大圈,最终还是回到了application文件中,还记得上一篇博文中介绍的registerConfiguredProviders方法吗?

    application的registerConfiguredProviders()方法对服务提供者进行了注册,通过框架的文件系统收集了配置文件中的各种provicers并转化成数组,在G:wamp64www estlaravel55vendorlaravelframeworksrcIlluminateFoundationProviderRepository.php类的load方法中进行加载,但最终还是会在application类中的register()方法中通过字符串的方式new出对象,在执行provider中自带的register()方法

     1     public function registerConfiguredProviders()
     2     {
     3         //laravel的集合类,将之前初始化时存入的config中的数组取出
     4         $providers = Collection::make($this->config['app.providers'])
     5                         ->partition(function ($provider) {
     6                             //并过滤出系统providers
     7                             return Str::startsWith($provider, 'Illuminate\');
     8                         });
     9         //之前在registerBaseBindings方法中绑定在PackageManifest类中的providers数组拼接,通过load方法加载它们
    10         $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);
    11         //new了provider库,传入服务容器、文件系统操作对象、与之前缓存的服务提供者路径
    12         (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
    13                     ->load($providers->collapse()->toArray());
    14     }
     1  //IlluminateFoundationProviderRepository.php
     2 
     3    public function load(array $providers)
     4     {
     5         // 查看bootstrap/cache/services.php有没有这个缓存文件
     6         // 第一次启动时是没有的
     7         $manifest = $this->loadManifest();
     8         // 开始没有这个缓存文件,那就把$providers[ ]里的值
     9         if ($this->shouldRecompile($manifest, $providers)) {
    10             // 然后根据$providers[ ]编译出services.php这个缓存文件
    11             $manifest = $this->compileManifest($providers);
    12         }
    13 
    14         foreach ($manifest['when'] as $provider => $events) {
    15             // 注册包含有事件监听的service provider
    16             // 包含有事件监听的service provider都要有when()函数返回
    17             $this->registerLoadEvents($provider, $events);
    18         }
    19 
    20         foreach ($manifest['eager'] as $provider) {
    21             // 把'eager'字段中service provider注册进容器中,
    22             // 即遍历每一个service provider,调用其中的register()方法
    23             // 向容器中注册具体的服务
    24             $this->app->register($this->createProvider($provider));
    25         }
    26 
    27         // 注册延迟的service provider,
    28         // deferred的service provider, 一是要设置$defer = true,二是要提供provides()方法返回绑定到容器中服务的名称
    29         $this->app->addDeferredServices($manifest['deferred']);
    30     }

    而boot操作就更简单了

     1     public function boot()
     2     {
     3         if ($this->booted) {
     4             return;
     5         }
     6 
     7         // Once the application has booted we will also fire some "booted" callbacks
     8         // for any listeners that need to do work after this initial booting gets
     9         // finished. This is useful when ordering the boot-up processes we run.
    10         //调用引导方法的钩子函数
    11         $this->fireAppCallbacks($this->bootingCallbacks);
    12         //使每个provider运行bootProvider,$p为provider
    13         array_walk($this->serviceProviders, function ($p) {
    14             $this->bootProvider($p);
    15         });
    16         //改变引导状态
    17         $this->booted = true;
    18         //调用引导方法的钩子函数
    19         $this->fireAppCallbacks($this->bootedCallbacks);
    20     }
    21 
    22     protected function bootProvider(ServiceProvider $provider)
    23     {
    24         //判断传入的provier,运行它们的boot方法完成引导
    25         if (method_exists($provider, 'boot')) {
    26             return $this->call([$provider, 'boot']);
    27         }
    28     }

    到这里,provider通过register注册在了服务容器内,provider的初始化工作也由boot函数完成,这个provider所提供的对象便可以直接拿来使用了。

    还记得学习laravel框架使用方式的时候,文档建议我们把所有在应用初始化时需要完成的事情,都写在AppServiceProvider的boot方法里吗?看到这里我们能明白作为系统核心prvider的app是最早被加载的,因此也充当了一个钩子函数的角色。

    在了解了provider的注册流程之后,就可以自己来自定义一个provider了。我们上一篇博客里还有一个契约的概念没有说明,这里简单举一个小例子来说明。

    1、新建一个接口。

    1 namespace AppContracts;
    2 
    3 interface Test
    4 {
    5     public function doing();
    6 }

    2、新建两个接口的实现

     1 namespace AppServices;
     2 
     3 use AppContractsTest;
     4 
     5 class TestService implements Test
     6 {
     7     public function doing()
     8     {
     9         echo 'this is TestService';
    10     }
    11 }
    12 
    13 
    14 namespace AppServices;
    15 
    16 use AppContractsTest;
    17 
    18 class SecondTestService implements Test
    19 {
    20     public function doing()
    21     {
    22         echo 'this is SecondTestService';
    23     }
    24 }

    3、新建一个provider,可使用artisan 命令行   php artisan make:provider TestServiceProvider 创建一个provider,契约上下文就在这个地方进行绑定。上一篇博文里讲到make方法的时候,容器在解析类的时候,有一个获取上下文的步骤,所要获取的concrete就是在provider中通过when方法绑定的类了,不过可惜这个绑定只能具体到类,不能具体到方法。

     1 namespace AppProviders;
     2 
     3 use IlluminateSupportServiceProvider;
     4 
     5 class TestServiceProvider extends ServiceProvider
     6 {
     7     /**
     8      * Bootstrap any application services.
     9      *
    10      * @return void
    11      */
    12     public function boot()
    13     {
    14         //
    15     }
    16 
    17     public function register()
    18     {
    19         $this->app->bind('AppContractsTest', 'AppservicesTestService');
    20         //重点在于when方法确定运行环境,也就是执行上下文,needs为make所需的abstract类名或别名,give所传入的参数则是实际调用的实现类了
    21         $this->app->when('AppHttpControllersIndexController')
    22                 ->needs('AppContractsTest')
    23                 ->give('AppServicesSecondTestService');
    24     }
    25 }

    4、在config/app.php文件的providers数组中添加刚刚生成的provider

     1     'providers' => [
     2 
     3         /*
     4          * Laravel Framework Service Providers...
     5          */
     6         IlluminateAuthAuthServiceProvider::class,
     7         IlluminateBroadcastingBroadcastServiceProvider::class,
     8         IlluminateBusBusServiceProvider::class,
     9         IlluminateCacheCacheServiceProvider::class,
    10         IlluminateFoundationProvidersConsoleSupportServiceProvider::class,
    11         IlluminateCookieCookieServiceProvider::class,
    12         IlluminateDatabaseDatabaseServiceProvider::class,
    13         IlluminateEncryptionEncryptionServiceProvider::class,
    14         IlluminateFilesystemFilesystemServiceProvider::class,
    15         IlluminateFoundationProvidersFoundationServiceProvider::class,
    16         IlluminateHashingHashServiceProvider::class,
    17         IlluminateMailMailServiceProvider::class,
    18         IlluminateNotificationsNotificationServiceProvider::class,
    19         IlluminatePaginationPaginationServiceProvider::class,
    20         IlluminatePipelinePipelineServiceProvider::class,
    21         IlluminateQueueQueueServiceProvider::class,
    22         IlluminateRedisRedisServiceProvider::class,
    23         IlluminateAuthPasswordsPasswordResetServiceProvider::class,
    24         IlluminateSessionSessionServiceProvider::class,
    25         IlluminateTranslationTranslationServiceProvider::class,
    26         IlluminateValidationValidationServiceProvider::class,
    27         IlluminateViewViewServiceProvider::class,
    28 
    29         /*
    30          * Package Service Providers...
    31          */
    32 
    33         /*
    34          * Application Service Providers...
    35          */
    36         AppProvidersAppServiceProvider::class,
    37         AppProvidersAuthServiceProvider::class,
    38         // AppProvidersBroadcastServiceProvider::class,
    39         AppProvidersEventServiceProvider::class,
    40         AppProvidersRouteServiceProvider::class,
    41         //添加刚刚生成的provider
    42         AppProvidersTestServiceProvider::class,
    43     ],

    5、在IndexController文件中添加执行代码

     1 namespace AppHttpControllers;
     2 
     3 use AppContractsTest;
     4 
     5 class IndexController extends Controller
     6 {
     7 
     8     public function __construct(Test $test)
     9     {
    10         $this->test = $test;
    11     }
    12 
    13     public function index(Test $test)
    14     {
    15         app()->make('AppContractsTest')->doing();
    16 
    17         echo '<br>';
    18         //只有通过构造方法进行自动加载依赖的方式才能触发契约的when绑定
    19         $this->test->doing();
    20     
    21         echo '<br>';
    22     //因为laravel中的上下文绑定只能具体到类,所以这里的$test实例依然为普通绑定
    23         $test->doing();
    24 
    25     }
    26 }

    运行后,会发现只有通过构造函数实例化的对象,才能触发额外的分支绑定。通过这个小例子,我们可以很清楚的理解契约了,就是在不同情况下的一个对接口的动态调用,算是java中多态和策略模式的另一实现方式。使用了这种实现方式,可以使我们在开发过程中的代码更加灵活,在改变实现方式的时候,只需改变provider中的实现绑定,即可快速实现需求变更。

    可能有人会发现我们的demo在执行时需要显示的使用make方法,一点也不优雅,这和laravel所宣扬的思想还是有差距。那是因为还有一个facade门面功能还没有用上,后面我们会来探寻一下facade到底是个什么东西。

  • 相关阅读:
    vi使用方法详细介绍
    Jenkins实现Android自动化打包
    JSON知识总结
    React Native中pointerEvent属性
    从零学React Native之06flexbox布局
    Android Http实现文件的上传和下载
    从零学React Native之05混合开发
    React Native声明属性和属性确认
    从零学React Native之04自定义对话框
    Android 在图片的指定位置添加标记
  • 原文地址:https://www.cnblogs.com/wyycc/p/9866834.html
Copyright © 2011-2022 走看看