• Laravel ServiceProvider注册过程及简单使用


    Laravel ServiceProvider注册过程及简单使用

    还记得facade注册流程吗?回顾下

    在bootstrap/app.php中返回$app实例后,通过singleton方法绑定了三个实现,然后将$app返回给了index.php,在index.php中尝试解析了http kernel,并且调用了kernel的handle方法(传递了请求实例),将通过handle方法返回的reponse返回给客户端,其中facade就是在处理请求的过程中注册的,同样的serviceprovider注册就在facade注册后面。

    还记得这个数组吗?

    protected $bootstrappers = [

    ​ IlluminateFoundationBootstrapLoadEnvironmentVariables::class,

    ​ IlluminateFoundationBootstrapLoadConfiguration::class,

    ​ IlluminateFoundationBootstrapHandleExceptions::class,

    ​ IlluminateFoundationBootstrapRegisterFacades::class,

    ​ IlluminateFoundationBootstrapRegisterProviders::class,

    ​ IlluminateFoundationBootstrapBootProviders::class,

    ];

    上篇讲解了RegisterFacades类

    此文只讲解RegisterProviders类,至于BootProviders就留给大家自己追吧,其实就是调用服务提供者中可能存在的boot方法

    • IlluminateFoundationHttpKernel::handle方法中
    protected function sendRequestThroughRouter($request)
    {
        $this->app->instance('request', $request);
    
        Facade::clearResolvedInstance('request');
    	
        // 跳转到bootstrap方法
        $this->bootstrap();
    
        return (new Pipeline($this->app))
            ->send($request)
            ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
            ->then($this->dispatchToRouter());
    }
    
    public function bootstrap()
    {   
        if (! $this->app->hasBeenBootstrapped()) {
            // bootstrapWith传递的就是上面的数组
            $this->app->bootstrapWith($this->bootstrappers());
        }
    }
    
    public function bootstrapWith(array $bootstrappers)
    {   
        $this->hasBeenBootstrapped = true;
    
        foreach ($bootstrappers as $bootstrapper) {
            $this['events']->dispatch('bootstrapping: ' . $bootstrapper, [$this]);
    		// 此文中$bootstrapper=IlluminateFoundationBootstrapRegisterProviders
            $this->make($bootstrapper)->bootstrap($this);
    
            $this['events']->dispatch('bootstrapped: ' . $bootstrapper, [$this]);
        }
    }
    // 以上代码上篇已经说过,此处再走一遍流程
    
    /**
     * Bootstrap the given application.
     *
     * @param  IlluminateContractsFoundationApplication  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {	
        // 跳转到registerConfiguredProviders方法,今天要说的都在这个方法里了
        $app->registerConfiguredProviders();
    }
    
    public function registerConfiguredProviders()
    {   
    	// 访问了一个不存在的config属性,触发Container的__get方法 跳转一下
        // Collection使用了EnumeratesValues trait,其中make方法返回Collection实例
        // 我们携带app.providers数组跳转到Collection的构造方法
        // 在此打印下Collection::make($this->config['app.providers'])
        IlluminateSupportCollection {#45 ▼
          #items: array:27 [▼
            0 => "IlluminateAuthAuthServiceProvider"
            1 => "IlluminateBroadcastingBroadcastServiceProvider"
            2 => "IlluminateBusBusServiceProvider"
    		...
            25 => "AppProvidersRouteServiceProvider"
            26 => "AppProvidersMyFacadeProvider"
          ]
        }
        // 链式调用partition方法 跳转一下
        $providers = Collection::make($this->config['app.providers'])
            ->partition(function ($provider) {
                return strpos($provider, 'Illuminate\') === 0;
            });
        // 在此打印$providers
        IlluminateSupportCollection {#32 ▼
          #items: array:2 [▼
            0 => IlluminateSupportCollection {#43 ▼
              #items: array:22 [▼
                0 => "IlluminateAuthAuthServiceProvider"
                ...
                21 => "IlluminateViewViewServiceProvider"
              ]
            }
            1 => IlluminateSupportCollection {#31 ▼
              #items: array:5 [▼
                22 => "AppProvidersAppServiceProvider"
                ...
                26 => "AppProvidersMyFacadeProvider"
              ]
            }
          ]
        }
        
        // [$this->make(PackageManifest::class)->providers()此方法以数组的形式返回扩展包中的服务提供者
        // 跳转到splice方法
        $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);
        dump($provider);
         IlluminateSupportCollection {#32 ▼
          #items: array:3 [▼
            0 => IlluminateSupportCollection {#43 ▼
              #items: array:22 [▼
                0 => "IlluminateAuthAuthServiceProvider"
    			...
                21 => "IlluminateViewViewServiceProvider"
              ]
            }
            1 => array:5 [▼
              0 => "FacadeIgnitionIgnitionServiceProvider"
    		  ...
              4 => "NunoMaduroCollisionAdaptersLaravelCollisionServiceProvider"
            ]
            2 => IlluminateSupportCollection {#31 ▼
              #items: array:5 [▼
                22 => "AppProvidersAppServiceProvider"
                ...
                26 => "AppProvidersMyFacadeProvider"
              ]
            }
          ]
        }
        
        
        // ProviderRepository 此类是真正注册service provider的地方
        // $this->getCachedServicesPath() 默认返回的是/bootstrap/cache/services.php绝对路径
        // 我们先看load方法中的参数,就是$provider属性中的所有数组元素,合并到一个大数组中并返回
        // 跳转到ProviderRepository类的构造方法
        (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
        ->load($providers->collapse()->toArray());
    }
    
    public function __get($key)
    {	
        // 上篇讲过Container实现了ArrayAccess接口
        // 继续触发offsetGet方法 跳转一下
        // 聪明的道友可能已经想到可不可以通过__set方法直接绑定实现到容器呢? 当然可以
        // 有兴趣的可以自行查看__set方法
        return $this[$key];
    }
    
    public function offsetGet($key)
    {	
        // 到此解析出了config实例 跳转回registerConfiguredProviders方法
        return $this->make($key);
    }
    
    IlluminateSupportCollection文件
    public function __construct($items = [])
    {   
        $this->items = $this->getArrayableItems($items);
    }
    
    IlluminateSupportTraitsEnumeratesValues文件
    public function partition($key, $operator = null, $value = null)
    {
        $passed = [];
        $failed = [];
    	// 调用partition方法的时候,只传递了一个闭包
        $callback = func_num_args() === 1
    		// 自己追下代码,$callable就是传递进来的闭包
            ? $this->valueRetriever($key)
            : $this->operatorForWhere(...func_get_args());
    
        foreach ($this as $key => $item) {
            // dump($key, $item);
            // 在此将service provider进行了分组,分组规则就在闭包中
            if ($callback($item, $key)) {
                $passed[$key] = $item;
            } else {
                $failed[$key] = $item;
            }
        }
    	// 返回到registerConfiguredProviders方法并打印$providers
        return new static([new static($passed), new static($failed)]);
    }
    
    IlluminateSupportCollection文件
    public function splice($offset, $length = null, $replacement = [])
    {
        if (func_num_args() === 1) {
            return new static(array_splice($this->items, $offset));
        }
        // array_splice 第三个参数为0表示在offset后面添加数组元素
        // 返回到registerConfiguredProviders方法并打印$providers
        return new static(array_splice($this->items, $offset, $length, $replacement));
    }
    
    IlluminateFoundationProviderRepository文件中
    public function __construct(ApplicationContract $app, Filesystem $files, $manifestPath)
    {
        $this->app = $app;
        $this->files = $files;
        $this->manifestPath = $manifestPath;
    }
    
    /**
     * Register the application service providers.
     *
     * @param  array  $providers
     * @return void
     */
    public function load(array $providers)
    {
    	// 跳转到loadManifest方法
        $manifest = $this->loadManifest();
        
        // First we will load the service manifest, which contains information on all
        // service providers registered with the application and which services it
        // provides. This is used to know which services are "deferred" loaders.
        
        // 判断是否需要重新编译providers
        // 比较$manifest 和 $providers
        // 如果不同
        if ($this->shouldRecompile($manifest, $providers)) {
            // 若想进入此逻辑,可以手动删除bootstrap/cache下的services.php文件
            // 让app重新缓存 跳转到compileManifest方法
            $manifest = $this->compileManifest($providers);
        }
    
        // Next, we will register events to load the providers for each of the events
        // that it has requested. This allows the service provider to defer itself
        // while still getting automatically loaded when a certain event occurs.
        foreach ($manifest['when'] as $provider => $events) {
            // 注册事件 以后讲解
            $this->registerLoadEvents($provider, $events);
        }
    
        // We will go ahead and register all of the eagerly loaded providers with the
        // application so their services can be registered with the application as
        // a provided service. Then we will set the deferred service list on it.
        // dump($manifest); die;
        foreach ($manifest['eager'] as $provider) {
            // 如果不是延迟加载服务,那就在此阶段(也就是框架引导阶段直接调用服务的register方法)
            // 跳转到app的register方法 前面文章也有讲到
            $this->app->register($provider);
        }
    	
        // 跳转到addDeferredServices方法,传递的数组都是要延迟加载的服务提供者
        $this->app->addDeferredServices($manifest['deferred']);
        // public function addDeferredServices(array $services)
        // {
        //     $this->deferredServices = array_merge($this->deferredServices, $services);
        // }
        // 下面来看Application的make方法
    }
    
    public function loadManifest()
    {
        // The service manifest is a file containing a JSON representation of every
        // service provided by the application and whether its provider is using
        // deferred loading or should be eagerly loaded on each request to us.
    
        // load方法会判断文件是否存在
        // 如果/bootstrap/cache/services.php存在就加载其中的数组
        if ($this->files->exists($this->manifestPath)) {
            $manifest = $this->files->getRequire($this->manifestPath);
            // dump($manifest);
            array:4 [▼
              "providers" => array:32 [▼
                0 => "IlluminateAuthAuthServiceProvider"
                ...
              ]
              "eager" => array:19 [▼
                0 => "IlluminateAuthAuthServiceProvider"
                ...
              ]
              "deferred" => array:105 [▼
                "IlluminateBroadcastingBroadcastManager" => 				"IlluminateBroadcastingBroadcastServiceProvider"
                ...
              ]
              "when" => array:13 [▼
                "IlluminateBroadcastingBroadcastServiceProvider" => []
                ...
              ]
            ]
            if ($manifest) {
                // 返回到load方法
                return array_merge(['when' => []], $manifest);
            }
        }
    }
    
    // 比较文件缓存和传递的$providers是否相等 跳转回load方法
    public function shouldRecompile($manifest, $providers)
    {
        // dump($manifest, $providers);
        return is_null($manifest) || $manifest['providers'] != $providers;
    }
    
    protected function compileManifest($providers)
    {
        // freshManifest方法返回数组 因为需要重新编译provider所以将when删除了
        // ['providers' => $providers, 'eager' => [], 'deferred' => []];
        $manifest = $this->freshManifest($providers);
        // 此时的$providers就是除了框架实例化时就注册的全部服务提供者了
        foreach ($providers as $provider) {
            // createProvider方法 return new $provider($this->app);
            $instance = $this->createProvider($provider);
            
            // When recompiling the service manifest, we will spin through each of the
            // providers and check if it's a deferred provider or not. If so we'll
            // add it's provided services to the manifest and note the provider.
    		
            // 我们所有的Provider都继承自ServiceProvider基类
            // 如果实现了DeferrableProvider的provides接口就认为此服务要延迟加载
            if ($instance->isDeferred()) {
                // 判断一个服务是否延迟加载 需要实现IlluminateContractsSupportDeferrableProvider接口的provides方法 返回一个数组
                foreach ($instance->provides() as $service) {
                    // 老版本的laravel 如果想表明一个延迟服务 需要在对应的provider中 加上protected $defer = true属性
                    // 现版本需要在provider中实现DeferrableProvider接口
                    // 查看 IlluminateRedisRedisServiceProvider 其中的provides方法
                    // public function provides()
                    // {
                    //     return ['redis', 'redis.connection'];
                    // }
                    $manifest['deferred'][$service] = $provider;
                }
    			// 如果provider没有实现when方法 基类返回[]
                $manifest['when'][$provider] = $instance->when();
            }
    
            // If the service providers are not deferred, we will simply add it to an
            // array of eagerly loaded providers that will get registered on every
            // request to this application instead of "lazy" loading every time.
            else {
                // 如果不是一个延迟服务 那么直接将服务放到eager数组中
                $manifest['eager'][] = $provider;
            }
        }
        // 将服务提供者写入文件 跳转回load方法
        return $this->writeManifest($manifest);
    }
    
    IlluminateFoundationApplication文件
    public function register($provider, $force = false)
    {   
        if (($registered = $this->getProvider($provider)) && !$force) {
            return $registered;
        }
    
        // If the given "provider" is a string, we will resolve it, passing in the
        // application instance automatically for the developer. This is simply
        // a more convenient way of specifying your service provider classes.
    
        // 可以看到register方法 是官方更加推荐的注册服务提供者的方式
        if (is_string($provider)) {
            // new一个provider
            $provider = $this->resolveProvider($provider);
        }
    
        // 调用服务提供者中的register方法
        $provider->register();
    
        // If there are bindings / singletons set as properties on the provider we
        // will spin through them and register them with the application, which
        // serves as a convenience layer while registering a lot of bindings.
    
        // 按道理来说每个serviceprovider都可以拥有这两个属性
        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);
        
        // If the application has already booted, we will call this boot method on
        // the provider class so it has an opportunity to do its boot logic and
        // will be ready for any usage by this developer's application logic.
    
        // 如果框架已经boot完毕 那么给后注册的provider执行boot方法
        if ($this->isBooted()) {
            // 还记得protected $booted属性在哪里设置为true的吗
            // 就在开篇的bootstrapWith方法中 表示框架已经引导完毕
            // 执行provider中的boot方法
            $this->bootProvider($provider);
        }
    	// 跳转回load方法
        return $provider;
    }
    
    /**
     * Resolve the given type from the container.
     *
     * (Overriding Container::make) 
     *
     * @param  string  $abstract
     * @param  array  $parameters
     * @return mixed
     */
    // 毫无疑问 重写了Container的make方法
    public function make($abstract, array $parameters = [])
    {   
        $abstract = $this->getAlias($abstract);
    	
        if ($this->isDeferredService($abstract) && !isset($this->instances[$abstract])) {
            // 跳转吧
            $this->loadDeferredProvider($abstract);
        }
    
        return parent::make($abstract, $parameters);
    }
    
    public function loadDeferredProvider($service)
    {
        if (!$this->isDeferredService($service)) {
            return;
        }
    
        $provider = $this->deferredServices[$service];
    
        // If the service provider has not already been loaded and registered we can
        // register it with the application and remove the service from this list
        // of deferred services, since it will already be loaded on subsequent.
        if (!isset($this->loadedProviders[$provider])) {
            // 跳转吧
            $this->registerDeferredProvider($provider, $service);
        }
    }
    
    public function registerDeferredProvider($provider, $service = null)
    {
        // Once the provider that provides the deferred service has been registered we
        // will remove it from our local list of the deferred services with related
        // providers so that this container does not try to resolve it out again.
        if ($service) {
            unset($this->deferredServices[$service]);
        }
    	// 可以看到最终还是调用了register方法 完结!!!
        $this->register($instance = new $provider($this));
    
        if (!$this->isBooted()) {
            $this->booting(function () use ($instance) {
                $this->bootProvider($instance);
            });
        }
    }
    
    简单测试
    Route::get('redis', function(){
        dump(resolve('app')->getDeferredServices());
        // 使用延迟解析
        dump(app()->app->getLoadedProviders()); // 没有redis对应的RedisServiceProvider
        dump(get_class(resolve('redis')));
        dump(app()['app']->getLoadedProviders()); // 有redis对应的ServiceProvider
    });
    

    到此服务注册算是完结了。

    下集预告: 传递到handle方法中的$request到底是啥及laravel中的管道。

  • 相关阅读:
    【个人实战】作品展播BI大屏【部分见github主页】
    JAVA设计模式之单例(singleton)
    你所不知道的redis安装方法,穿一手鞋,看一手资料
    zookeeper实现分布式锁总结,看这一篇足矣(设计模式应用实战)
    JAVA设计模式之状态模式(state)
    JAVA设计模式之适配器模式(adapter)
    JAVA设计模式之构建器模式(builder)
    Redis实现分布式锁(设计模式应用实战)
    JAVA设计模式之享元模式(flyweight)
    JAVA设计模式之组合模式(composite)
  • 原文地址:https://www.cnblogs.com/alwayslinger/p/13434374.html
Copyright © 2020-2023  润新知