最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Laravel 走进服务提供者的世界

    正文概述 转载于:掘金(Rocket)   2021-06-11   74

    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\Applicationmake

        /**
         * 从容器中解析出给定服务
         */
        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、流程图

    Laravel  走进服务提供者的世界

    5、结尾

    与诸君共勉之,希望对您有所帮助
    

    起源地 » Laravel 走进服务提供者的世界

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元