成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

深入剖析 Laravel 服務(wù)提供者實(shí)現(xiàn)原理

yeooo / 466人閱讀

摘要:服務(wù)提供者啟動(dòng)原理之前我們有學(xué)習(xí)深度挖掘生命周期和深入剖析服務(wù)容器,今天我們將學(xué)習(xí)服務(wù)提供者。的所有核心服務(wù)都是通過(guò)服務(wù)提供者進(jìn)行引導(dǎo)啟動(dòng)的,所以想深入了解那么研究服務(wù)提供者的原理是個(gè)繞不開(kāi)的話(huà)題。

本文首發(fā)于 深入剖析 Laravel 服務(wù)提供者實(shí)現(xiàn)原理,轉(zhuǎn)載請(qǐng)注明出處。

今天我們將學(xué)習(xí) Laravel 框架另外一個(gè)核心內(nèi)容「服務(wù)提供者(Service Provider)」。服務(wù)提供者的功能是完成 Laravel 應(yīng)用的引導(dǎo)啟動(dòng),或者說(shuō)是將 Laravel 中的各種服務(wù)「注冊(cè)」到「Laravel 服務(wù)容器」,這樣才能在后續(xù)處理 HTTP 請(qǐng)求時(shí)使用這些服務(wù)。

服務(wù)提供者基本概念

我們知道 「服務(wù)提供者」是配置應(yīng)用的中心,它的主要工作是使用「服務(wù)容器」實(shí)現(xiàn)服務(wù)容器綁定、事件監(jiān)聽(tīng)器、中間件,甚至是路由的注冊(cè)。

除核心服務(wù)外,幾乎所有的服務(wù)提供者都定義在配置文件 config/app.php 文件中的 providers 節(jié)點(diǎn)中。

服務(wù)提供者的典型處理流程是,當(dāng)接 Laravel 應(yīng)用接收到 HTTP 請(qǐng)求時(shí)會(huì)去執(zhí)行「服務(wù)提供者的 register(注冊(cè))」方法,將各個(gè)服務(wù)「綁定」到容器內(nèi);之后,到了實(shí)際處理請(qǐng)求階段,依據(jù)使用情況按需加載所需服務(wù)。這樣的優(yōu)勢(shì)很明顯能夠提升應(yīng)用的性能。

細(xì)心的朋友可能發(fā)現(xiàn)這里用了一個(gè)詞「幾乎」,沒(méi)錯(cuò)還有一些屬于核心服務(wù)提供者,這些并沒(méi)有定義在 providers 配置節(jié)點(diǎn)中而是直接由 IlluminateFoundationApplication 服務(wù)容器直接在實(shí)例化階段就完成了注冊(cè)服務(wù)。

registerBaseServiceProviders();
        ...
    }

    /**
     * Register all of the base service providers. 注冊(cè)應(yīng)用基礎(chǔ)服務(wù)提供者
     *
     * @return void
     */
    protected function registerBaseServiceProviders()
    {
        $this->register(new EventServiceProvider($this));

        $this->register(new LogServiceProvider($this));

        $this->register(new RoutingServiceProvider($this));
    }

對(duì)服務(wù)容器不是很熟悉的老鐵可以閱讀 深入剖析 Laravel 服務(wù)容器,并且在文中「注冊(cè)基礎(chǔ)服務(wù)提供者」一節(jié)也有詳細(xì)分析服務(wù)容器是如何注冊(cè)服務(wù)提供者的。

另外一個(gè),我們還需要了解的是所有的服務(wù)提供者都繼承自 IlluminateSupportServiceProvider 類(lèi)。不過(guò)對(duì)于我們來(lái)說(shuō)目前還無(wú)需研究基類(lèi),所以我們將焦點(diǎn)放到如何實(shí)現(xiàn)一個(gè)自定義的服務(wù)提供者,然后還有兩個(gè)需要掌握方法。

服務(wù)提供者入門(mén) 創(chuàng)建自定義服務(wù)提供者

要?jiǎng)?chuàng)建自定義的「服務(wù)提供者」,可以直接使用 Laravel 內(nèi)置的 artisan 命令完成。

php artisan make:provider RiskServiceProvider

這個(gè)命令會(huì)在 app/Providers 目錄下創(chuàng)建 RiskServiceProvider.php 文件,打開(kāi)文件內(nèi)容如下:


register 方法

register 方法中,我們無(wú)需處理業(yè)務(wù)邏輯,在這個(gè)方法中你只需去處理「綁定」服務(wù)到服務(wù)容器中即可。

從文檔中我們知道:

register 方法中,你只需要將類(lèi)綁定到 服務(wù)容器 中。而不需要嘗試在 register 方法中注冊(cè)任何事件監(jiān)聽(tīng)器、路由或者任何其他功能。否則,你可能會(huì)意外使用到尚未加載的服務(wù)提供器提供的服務(wù)。

如何理解這句話(huà)的含義呢?

如果你有了解過(guò)服務(wù)容器運(yùn)行原理,就會(huì)知道在「綁定」操作僅僅是建立起接口和實(shí)現(xiàn)的對(duì)應(yīng)關(guān)系,此時(shí)并不會(huì)創(chuàng)建具體的實(shí)例,即不會(huì)存在真實(shí)的依賴(lài)關(guān)系。直到某個(gè)服務(wù)真的被用到時(shí)才會(huì)從「服務(wù)容器」中解析出來(lái),而解析的過(guò)程發(fā)生在所有服務(wù)「注冊(cè)」完成之后。

一旦我們嘗試在 register 注冊(cè)階段使用某些未被加載的服務(wù)依賴(lài),即這個(gè)服務(wù)目前還沒(méi)有被注冊(cè)所以不可用。

這樣就需要在「注冊(cè)」綁定時(shí),同時(shí)需要關(guān)注服務(wù)的注冊(cè)順序,但這一點(diǎn) Laravel 并不作出任何保證。

理解了這個(gè)道理,我們就可以隨便進(jìn)入一個(gè)「服務(wù)提供者」來(lái)看看其中的 register 方法的邏輯,現(xiàn)在我們挑選的是 IlluminateCacheCacheServiceProvider 服務(wù)作為講解:

app->singleton("cache", function ($app) {
            return new CacheManager($app);
        });

        $this->app->singleton("cache.store", function ($app) {
            return $app["cache"]->driver();
        });

        $this->app->singleton("memcached.connector", function () {
            return new MemcachedConnector;
        });
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return [
            "cache", "cache.store", "memcached.connector",
        ];
    }
}

沒(méi)錯(cuò),如你所預(yù)料的一樣,它的 register 方法執(zhí)行了三個(gè)單例綁定操作,僅此而已。

簡(jiǎn)單注冊(cè)服務(wù)

對(duì)于處理復(fù)雜綁定邏輯,可以自定義「服務(wù)提供者」。但是如果是比較簡(jiǎn)單的注冊(cè)服務(wù),有沒(méi)有比較方便的綁定方法呢?畢竟,并不是每個(gè)服務(wù)都會(huì)有復(fù)雜的依賴(lài)處理。

我們可以從 文檔 中得到解答:

如果你的服務(wù)提供商注冊(cè)許多簡(jiǎn)單的綁定,你可能想使用 bindingssingletons 屬性而不是手動(dòng)注冊(cè)每個(gè)容器綁定。
 DigitalOceanServerProvider::class,
    ];

    /**
     * 設(shè)定單例模式的容器綁定對(duì)應(yīng)關(guān)系
     *
     * @var array
     */
    public $singletons = [
        DowntimeNotifier::class => PingdomDowntimeNotifier::class,
    ];
}

此時(shí),通過(guò) bingdingssingletons 成員變量來(lái)設(shè)置簡(jiǎn)單的綁定,就可以避免大量的「服務(wù)提供者」類(lèi)的生成了。

boot 方法

聊完了 register 方法,接下來(lái)進(jìn)入另一個(gè)主題,來(lái)研究一下服務(wù)提供者的 boot 方法。

通過(guò)前面的學(xué)習(xí),我們知道在 register 方法中 Laravel 并不能保證所有其他服務(wù)已被加載。所以當(dāng)需要處理具有依賴(lài)關(guān)系的業(yè)務(wù)邏輯時(shí),應(yīng)該將這些邏輯處理放置到 boot 方法內(nèi)。在 boot 方法中我們可以去完成:注冊(cè)事件監(jiān)聽(tīng)器、引入路由文件、注冊(cè)過(guò)濾器等任何你可以想象得到的業(yè)務(wù)處理。

config/app.php 配置中我們可以看到如下幾個(gè)服務(wù)提供者:

        /*
         * Application Service Providers...
         */
        AppProvidersAppServiceProvider::class,
        AppProvidersAuthServiceProvider::class,
        // AppProvidersBroadcastServiceProvider::class,
        AppProvidersEventServiceProvider::class,
        AppProvidersRouteServiceProvider::class,

選擇其中的 AppProvidersRouteServiceProvider::class 服務(wù)提供者它繼承自 IlluminateFoundationSupportProvidersRouteServiceProvider 基類(lèi)來(lái)看下:

// 實(shí)現(xiàn)類(lèi)
class RouteServiceProvider extends ServiceProvider
{
    /**
     * This namespace is applied to your controller routes. In addition, it is set as the URL generator"s root namespace.
     */
    protected $namespace = "AppHttpControllers";

    /**
     * Define your route model bindings, pattern filters, etc.
     */
    public function boot()
    {
        parent::boot();
    }

    /**
     * Define the routes for the application. 定義應(yīng)用路由
     */
    public function map()
    {
        $this->mapApiRoutes();
        $this->mapWebRoutes();
    }

    /**
     * Define the "web" routes for the application. These routes all receive session state, CSRF protection, etc.
     * 定義 web 路由。web 路由支持會(huì)話(huà)狀態(tài)和 CSRF 防御中間件等。
     */
    protected function mapWebRoutes()
    {
        Route::middleware("web")
             ->namespace($this->namespace)
             ->group(base_path("routes/web.php"));
    }

    /**
     * Define the "api" routes for the application. These routes are typically stateless.
     * 定義 api 路由。api 接口路由支持典型的  HTTP 無(wú)狀態(tài)協(xié)議。
     */
    protected function mapApiRoutes()
    {
        Route::prefix("api")
             ->middleware("api")
             ->namespace($this->namespace)
             ->group(base_path("routes/api.php"));
    }
}

基類(lèi) IlluminateFoundationSupportProvidersRouteServiceProvider:

//  基類(lèi)
namespace IlluminateFoundationSupportProviders;

/**
 * @mixin IlluminateRoutingRouter
 */
class RouteServiceProvider extends ServiceProvider
{
    /**
     * The controller namespace for the application.
     */
    protected $namespace;

    /**
     * Bootstrap any application services. 引導(dǎo)啟動(dòng)服務(wù)
     */
    public function boot()
    {
        $this->setRootControllerNamespace();

        // 如果已緩存路由,從緩存文件中載入路由
        if ($this->app->routesAreCached()) {
            $this->loadCachedRoutes();
        } else {
            //還沒(méi)有路由緩存,加載路由
            $this->loadRoutes();

            $this->app->booted(function () {
                $this->app["router"]->getRoutes()->refreshNameLookups();
                $this->app["router"]->getRoutes()->refreshActionLookups();
            });
        }
    }

    /**
     * Load the application routes. 加載應(yīng)用路由,調(diào)用實(shí)例的 map 方法,該方法定義在 AppProvidersRouteServiceProvider::class 中。
     */
    protected function loadRoutes()
    {
        if (method_exists($this, "map")) {
            $this->app->call([$this, "map"]);
        }
    }
}

對(duì)于 RouteServiceProvider 來(lái)講,它的 boot 方法在處理一個(gè)路由載入的問(wèn)題:

判斷是否已有路由緩存;

有路由緩存,則直接載入路由緩存;

無(wú)路由緩存,執(zhí)行 map 方法載入路由。

感興趣的朋友可以自行了解下 Application Service Providers 配置節(jié)點(diǎn)的相關(guān)服務(wù)提供者,這邊不再贅述。

配置服務(wù)提供者

了解完「服務(wù)提供者」兩個(gè)重要方法后,我們還需要知道 Laravel 是如何查找到所有的服務(wù)提供者的。這個(gè)超找的過(guò)程就是去讀取 config/app.php 文件中的 providers 節(jié)點(diǎn)內(nèi)所有的「服務(wù)提供器」。

具體的讀取過(guò)程我們也會(huì)在「服務(wù)提供者啟動(dòng)原理」一節(jié)中講解。

延遲綁定服務(wù)提供者

對(duì)于一個(gè)項(xiàng)目來(lái)說(shuō),除了要讓它跑起來(lái),往往我們還需要關(guān)注它的性能問(wèn)題。

當(dāng)我們打開(kāi) config/app.php 配置文件時(shí),你會(huì)發(fā)現(xiàn)有配置很多服務(wù)提供者,難道所有的都需要去執(zhí)行它的 registerboot 方法么?

對(duì)于不會(huì)每次使用的服務(wù)提供者很明顯,無(wú)需每次注冊(cè)和啟動(dòng),直到需要用到它的時(shí)候。

為了解決這個(gè)問(wèn)題 Laravel 內(nèi)置支持 延遲服務(wù)提供者 功能,啟用時(shí)延遲功能后,當(dāng)它真正需要注冊(cè)綁定時(shí)才會(huì)執(zhí)行 register 方法,這樣就可以提升我們服務(wù)的性能了。

啟用「延遲服務(wù)提供者」功能,需要完成兩個(gè)操作配置:

在對(duì)應(yīng)服務(wù)提供者中將 defer 屬性設(shè)置為 true

并定義 provides 方法,方法返回在提供者 register 方法內(nèi)需要注冊(cè)的服務(wù)接口名稱(chēng)。

我們拿 config/app.php 配置中的 BroadcastServiceProvider 作為演示說(shuō)明:

app->singleton(BroadcastManager::class, function ($app) {
            return new BroadcastManager($app);
        });

        $this->app->singleton(BroadcasterContract::class, function ($app) {
            return $app->make(BroadcastManager::class)->connection();
        });

        $this->app->alias(
            BroadcastManager::class, BroadcastingFactory::class
        );
    }

    /**
     * Get the services provided by the provider. 獲取提供者所提供的服務(wù)接口名稱(chēng)。
     */
    public function provides()
    {
        return [
            BroadcastManager::class,
            BroadcastingFactory::class,
            BroadcasterContract::class,
        ];
    }
}
小結(jié)

在「服務(wù)提供者入門(mén)」這個(gè)小節(jié)我們學(xué)習(xí)了服務(wù)提供者的基本使用和性能優(yōu)化相關(guān)知識(shí),包括:

如何創(chuàng)建自定義的服務(wù)提供者;

創(chuàng)建 register 方法注冊(cè)服務(wù)到 Laravel 服務(wù)容器;

創(chuàng)建 boot 方法啟動(dòng)服務(wù)提供者的引導(dǎo)程序;

配置我們的服務(wù)提供者到 config/app.php 文件,這樣才能在容器中加載相應(yīng)服務(wù);

通過(guò)延遲綁定技術(shù),提升 Laravel 服務(wù)性能。

下一小節(jié),我們將焦點(diǎn)轉(zhuǎn)移到「服務(wù)提供者」的實(shí)現(xiàn)原理中,深入到 Laravel 內(nèi)核中去探索「服務(wù)提供者」如何被注冊(cè)和啟動(dòng),又是如何能夠通過(guò)延遲技術(shù)提升 Laravel 應(yīng)用的性能的。

服務(wù)提供者啟動(dòng)原理

之前我們有學(xué)習(xí) 深度挖掘 Laravel 生命周期 和 深入剖析 Laravel 服務(wù)容器,今天我們將學(xué)習(xí)「服務(wù)提供者」。

Laravel 的所有核心服務(wù)都是通過(guò)服務(wù)提供者進(jìn)行引導(dǎo)啟動(dòng)的,所以想深入了解 Laravel 那么研究「服務(wù)提供者」的原理是個(gè)繞不開(kāi)的話(huà)題。

引導(dǎo)程序的啟動(dòng)流程

服務(wù)提供者 注冊(cè)引導(dǎo)啟動(dòng) 直到處理 HTTP 請(qǐng)求階段才開(kāi)始。所以我們直接進(jìn)入到 AppConsoleKernel::class 類(lèi),同時(shí)這個(gè)類(lèi)繼承于 IlluminateFoundationHttpKernel 類(lèi)。

IlluminateFoundationHttpKernel 類(lèi)中我們可以看到如下內(nèi)容:

class Kernel implements KernelContract
{
    ...

    /**
     * The bootstrap classes for the application. 應(yīng)用引導(dǎo)類(lèi)
     */
    protected $bootstrappers = [
        ...
        IlluminateFoundationBootstrapRegisterProviders::class, // 用于注冊(cè)(register)「服務(wù)提供者」的引導(dǎo)類(lèi)
        IlluminateFoundationBootstrapBootProviders::class, // 用于啟動(dòng)(boot)「服務(wù)提供者」的引導(dǎo)類(lèi)
    ];

    /**
     * Handle an incoming HTTP request. 處理 HTTP 請(qǐng)求
     */
    public function handle($request)
    {
        try {
            $request->enableHttpMethodParameterOverride();

            $response = $this->sendRequestThroughRouter($request);
        } catch (Exception $e) {
            ...
        } catch (Throwable $e) {
            ...
        }

        ...
    }

    /**
     * Send the given request through the middleware / router. 對(duì) HTTP 請(qǐng)求執(zhí)行中間件處理后再發(fā)送到指定路由。
     */
    protected function sendRequestThroughRouter($request)
    {
        ...

        // 1. 引導(dǎo)類(lèi)引導(dǎo)啟動(dòng)。
        $this->bootstrap();

        // 2. 中間件及請(qǐng)求處理,生成響應(yīng)并返回響應(yīng)。
        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }

    /**
     * Bootstrap the application for HTTP requests. 接收 HTTP 請(qǐng)求時(shí)啟動(dòng)應(yīng)用引導(dǎo)程序。
     */
    public function bootstrap()
    {
        // 引導(dǎo)類(lèi)啟動(dòng)由 Application 容器引導(dǎo)啟動(dòng)。
        if (! $this->app->hasBeenBootstrapped()) {
            $this->app->bootstrapWith($this->bootstrappers());
        }
    }
}

IlluminateFoundationHttpKernel 我們的內(nèi)核處理 HTTP 請(qǐng)求時(shí)會(huì)經(jīng)過(guò)一下兩個(gè)主要步驟:

啟動(dòng)引導(dǎo)程序通過(guò) $this->bootstrap() 方法完成,其中包括所有服務(wù)提供者的注冊(cè)和引導(dǎo)處理;

處理 HTTP 請(qǐng)求(這個(gè)問(wèn)題涉及到中間件、路由及相應(yīng)處理,本文將不做深入探討)。

進(jìn)入 IlluminateFoundationApplication 容器中的 bootstrapWith() 方法,來(lái)看看容器是如何將引導(dǎo)類(lèi)引導(dǎo)啟動(dòng)的:

    /**
     * Run the given array of bootstrap classes. 執(zhí)行給定引導(dǎo)程序
     */
    public function bootstrapWith(array $bootstrappers)
    {
        $this->hasBeenBootstrapped = true;

        foreach ($bootstrappers as $bootstrapper) {
            $this["events"]->fire("bootstrapping: ".$bootstrapper, [$this]);

            // 從容器中解析出實(shí)例,然后調(diào)用實(shí)例的 bootstrap() 方法引導(dǎo)啟動(dòng)。 
            $this->make($bootstrapper)->bootstrap($this);

            $this["events"]->fire("bootstrapped: ".$bootstrapper, [$this]);
        }
    }

通過(guò)服務(wù)容器的 bootstrap() 方法引導(dǎo)啟動(dòng)時(shí),將定義的在 IlluminateFoundationHttpKerne 類(lèi)中的應(yīng)用引導(dǎo)類(lèi)($bootstrappers)交由 Application 服務(wù)容器引導(dǎo)啟動(dòng)。其中與「服務(wù)提供者」有關(guān)的引導(dǎo)類(lèi)為:

當(dāng) IlluminateFoundationHttpKerne HTTP 內(nèi)核通過(guò) bootstrap() 方法引導(dǎo)啟動(dòng)時(shí),實(shí)際由服務(wù)容器(Application)去完成引導(dǎo)啟動(dòng)的工作,并依據(jù)定義在 HTTP 內(nèi)核中的引導(dǎo)類(lèi)屬性配置順序依次引導(dǎo)啟動(dòng),最終「服務(wù)提供者」的啟動(dòng)順序是:

執(zhí)行「服務(wù)提供者」register 方法的引導(dǎo)類(lèi):IlluminateFoundationBootstrapRegisterProviders::class,將完成所有定義在 config/app.php 配置中的服務(wù)提供者的注冊(cè)(register)處理;

執(zhí)行「服務(wù)提供者」boot 方法的引導(dǎo)類(lèi):IlluminateFoundationBootstrapBootProviders::class,將完成所有定義在 config/app.php 配置中的服務(wù)提供者的啟動(dòng)(boot)處理。

Laravel 執(zhí)行服務(wù)提供者注冊(cè)(register)處理

前面說(shuō)過(guò)「服務(wù)提供者」的注冊(cè)由 IlluminateFoundationBootstrapRegisterProviders::class 引導(dǎo)類(lèi)啟動(dòng)方法(botstrap())完成。

1. RegisterProviders 引導(dǎo)注冊(cè)
registerConfiguredProviders();
    }
}

在其通過(guò)調(diào)用服務(wù)容器的 registerConfiguredProviders() 方法完成引導(dǎo)啟動(dòng),所以我們需要到容器中一探究竟。

2. 由服務(wù)容器執(zhí)行配置文件中的所有服務(wù)提供者服務(wù)完成注冊(cè)。
    /**
     * Register all of the configured providers. 執(zhí)行所有配置服務(wù)提供者完成注冊(cè)處理。
     * 
     * @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Application.php
     */
    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()]);

        // 通過(guò)服務(wù)提供者倉(cāng)庫(kù)(ProviderRepository)加載所有的提供者。
        (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
                    ->load($providers->collapse()->toArray());
    }
3. 最后由服務(wù)提供者倉(cāng)庫(kù)(ProviderRepository)執(zhí)行服務(wù)提供者的注冊(cè)處理。
loadManifest();

        // 首先從服務(wù)提供者的緩存清單文件中載入服務(wù)提供者集合。其中包含「延遲加載」的服務(wù)提供者。
        if ($this->shouldRecompile($manifest, $providers)) {
            $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);
        }

        // 到這里,先執(zhí)行應(yīng)用必要(貪婪)的服務(wù)提供者完成服務(wù)注冊(cè)。
        foreach ($manifest["eager"] as $provider) {
            $this->app->register($provider);
        }

        // 最后將所有「延遲加載服務(wù)提供者」加入到容器中。
        $this->app->addDeferredServices($manifest["deferred"]);
    }

    /**
     * Compile the application service manifest file. 將服務(wù)提供者編譯到清單文件中緩存起來(lái)。
     */
    protected function compileManifest($providers)
    {
        // The service manifest should contain a list of all of the providers for
        // the application so we can compare it on each request to the service
        // and determine if the manifest should be recompiled or is current.
        $manifest = $this->freshManifest($providers);

        foreach ($providers as $provider) {
            // 解析出 $provider 對(duì)應(yīng)的實(shí)例
            $instance = $this->createProvider($provider);

            // 判斷當(dāng)前服務(wù)提供者是否為「延遲加載」類(lèi)行的,是則將其加入到緩存文件的「延遲加載(deferred)」集合中。
            if ($instance->isDeferred()) {
                foreach ($instance->provides() as $service) {
                    $manifest["deferred"][$service] = $provider;
                }

                $manifest["when"][$provider] = $instance->when();
            }

            // 如果不是「延遲加載」類(lèi)型的服務(wù)提供者,則為貪婪加載必須立即去執(zhí)行注冊(cè)方法。
            else {
                $manifest["eager"][] = $provider;
            }
        }

        // 將歸類(lèi)后的服務(wù)提供者寫(xiě)入清單文件。
        return $this->writeManifest($manifest);
    }

服務(wù)提供者倉(cāng)庫(kù)(ProviderRepository) 處理程序中依次執(zhí)行如下處理:

如果存在服務(wù)提供者緩存清單,則直接讀取「服務(wù)提供者」集合;

否則,將從 config/app.php 配置中的服務(wù)提供者編譯到緩存清單中;編譯由 compileManifest() 方法完成; 編譯緩存清單時(shí)將處理貪婪加載(eager)和延遲加載(deferred)的服務(wù)提供者;

對(duì)于貪婪加載的提供者直接執(zhí)行服務(wù)容器的 register 方法完成服務(wù)注冊(cè);

將延遲加載提供者加入到服務(wù)容器中,按需注冊(cè)和引導(dǎo)啟動(dòng)。

最后通過(guò) IlluminateFoundationApplication 容器完成注冊(cè)處理:

    /**
     * Register a service provider with the application. 在應(yīng)用服務(wù)容器中注冊(cè)一個(gè)服務(wù)提供者。
     */
    public function register($provider, $options = [], $force = false)
    {
        if (($registered = $this->getProvider($provider)) && ! $force) {
            return $registered;
        }

        // 如果給定的服務(wù)提供者是接口名稱(chēng),解析出它的實(shí)例。
        if (is_string($provider)) {
            $provider = $this->resolveProvider($provider);
        }

        // 服務(wù)提供者提供注冊(cè)方法時(shí),執(zhí)行注冊(cè)服務(wù)處理
        if (method_exists($provider, "register")) {
            $provider->register();
        }

        $this->markAsRegistered($provider);

        // 判斷 Laravel 應(yīng)用是否已啟動(dòng)。已啟動(dòng)的話(huà)需要去執(zhí)行啟動(dòng)處理。
        if ($this->booted) {
            $this->bootProvider($provider);
        }

        return $provider;
    }

為什么需要判斷是否已經(jīng)啟動(dòng)過(guò)呢?

因?yàn)閷?duì)于延遲加載的服務(wù)提供者只有在使用時(shí)才會(huì)被調(diào)用,所以這里需要這樣判斷,然后再去啟動(dòng)它。

以上,便是

Laravel 執(zhí)行服務(wù)提供者啟動(dòng)(boot)處理

「服務(wù)提供者」的啟動(dòng)流程和注冊(cè)流程大致相同,有興趣的朋友可以深入源碼了解一下。

1. BootProviders 引導(dǎo)啟動(dòng)
boot();
    }
}
2. 由服務(wù)容器執(zhí)行配置文件中的所有服務(wù)提供者服務(wù)完成啟動(dòng)。
    /**
     * Boot the application"s service providers. 引導(dǎo)啟動(dòng)應(yīng)用所有服務(wù)提供者
     *
     * @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Application.php
     */
    public function boot()
    {
        if ($this->booted) {
            return;
        }

        // Once the application has booted we will also fire some "booted" callbacks
        // for any listeners that need to do work after this initial booting gets
        // finished. This is useful when ordering the boot-up processes we run.
        $this->fireAppCallbacks($this->bootingCallbacks);

        // 遍歷并執(zhí)行服務(wù)提供者的 boot 方法。
        array_walk($this->serviceProviders, function ($p) {
            $this->bootProvider($p);
        });

        $this->booted = true;

        $this->fireAppCallbacks($this->bootedCallbacks);
    }

    /**
     * Boot the given service provider. 啟動(dòng)給定服務(wù)提供者
     */
    protected function bootProvider(ServiceProvider $provider)
    {
        if (method_exists($provider, "boot")) {
            return $this->call([$provider, "boot"]);
        }
    }

以上便是服務(wù)提供者執(zhí)行 注冊(cè)綁定服務(wù)引導(dǎo)啟動(dòng) 的相關(guān)實(shí)現(xiàn)。

但是稍等一下,我們是不是忘記了還有「延遲加載」類(lèi)型的服務(wù)提供者,它們還沒(méi)有被注冊(cè)和引導(dǎo)啟動(dòng)呢!

Laravel 如何完成延遲加載類(lèi)型的服務(wù)提供者

對(duì)于延遲加載類(lèi)型的服務(wù)提供者,我們要到使用時(shí)才會(huì)去執(zhí)行它們內(nèi)部的 registerboot 方法。這里我們所說(shuō)的使用即使需要 解析 它,我們知道解析處理由服務(wù)容器完成。

所以我們需要進(jìn)入到 IlluminateFoundationApplication 容器中探索 make 解析的一些細(xì)節(jié)。

    /**
     * Resolve the given type from the container. 從容器中解析出給定服務(wù)
     * 
     * @see https://github.com/laravel/framework/blob/5.6/src/Illuminate/Foundation/Application.php
     */
    public function make($abstract, array $parameters = [])
    {
        $abstract = $this->getAlias($abstract);

        // 判斷這個(gè)接口是否為延遲類(lèi)型的并且沒(méi)有被解析過(guò),是則去將它加載到容器中。
        if (isset($this->deferredServices[$abstract]) && ! isset($this->instances[$abstract])) {
            $this->loadDeferredProvider($abstract);
        }

        return parent::make($abstract, $parameters);
    }

    /**
     * Load the provider for a deferred service. 加載給定延遲加載服務(wù)提供者
     */
    public function loadDeferredProvider($service)
    {
        if (! isset($this->deferredServices[$service])) {
            return;
        }

        $provider = $this->deferredServices[$service];

        // 如果服務(wù)為注冊(cè)則去注冊(cè)并從延遲服務(wù)提供者集合中刪除它。
        if (! isset($this->loadedProviders[$provider])) {
            $this->registerDeferredProvider($provider, $service);
        }
    }

    /**
     * Register a deferred provider and service. 去執(zhí)行服務(wù)提供者的注冊(cè)方法。
     */
    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]);
        }

        // 執(zhí)行服務(wù)提供者注冊(cè)服務(wù)。
        $this->register($instance = new $provider($this));

        // 執(zhí)行服務(wù)提供者啟動(dòng)服務(wù)。
        if (! $this->booted) {
            $this->booting(function () use ($instance) {
                $this->bootProvider($instance);
            });
        }
    }
總結(jié)

今天我們深入研究了 Laravel 服務(wù)提供者的注冊(cè)和啟動(dòng)的實(shí)現(xiàn)原理,希望對(duì)大家有所幫助。

如果對(duì)如何自定義服務(wù)提供者不甚了解的朋友可以去閱讀 Laravel 服務(wù)提供者指南 這篇文章。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/28788.html

相關(guān)文章

  • 深入剖析 Laravel 服務(wù)容器

    摘要:劃下重點(diǎn),服務(wù)容器是用于管理類(lèi)的依賴(lài)和執(zhí)行依賴(lài)注入的工具。類(lèi)的實(shí)例化及其依賴(lài)的注入,完全由服務(wù)容器自動(dòng)的去完成。 本文首發(fā)于 深入剖析 Laravel 服務(wù)容器,轉(zhuǎn)載請(qǐng)注明出處。喜歡的朋友不要吝嗇你們的贊同,謝謝。 之前在 深度挖掘 Laravel 生命周期 一文中,我們有去探究 Laravel 究竟是如何接收 HTTP 請(qǐng)求,又是如何生成響應(yīng)并最終呈現(xiàn)給用戶(hù)的工作原理。 本章將帶領(lǐng)大...

    abson 評(píng)論0 收藏0
  • 深入淺出 Laravel 的 Facade 外觀系統(tǒng)

    摘要:外觀模式定義了一個(gè)高層接口,這個(gè)接口使得這一子系統(tǒng)更加容易使用。將使用者與子系統(tǒng)從直接耦合,轉(zhuǎn)變成由外觀類(lèi)提供統(tǒng)一的接口給使用者使用,以降低客戶(hù)端與子系統(tǒng)之間的耦合度。接下來(lái)將深入分析外觀服務(wù)的加載過(guò)程。引導(dǎo)程序?qū)⒃谔幚碚?qǐng)求是完成引導(dǎo)啟動(dòng)。 本文首發(fā)于 深入淺出 Laravel 的 Facade 外觀系統(tǒng),轉(zhuǎn)載請(qǐng)注明出處。 今天我們將學(xué)習(xí) Laravel 核心架構(gòu)中的另一個(gè)主題「Fac...

    KavenFan 評(píng)論0 收藏0
  • Swoft 源碼剖析 - 目錄

    摘要:作者鏈接來(lái)源簡(jiǎn)書(shū)著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。同時(shí)順手整理個(gè)人對(duì)源碼的相關(guān)理解,希望能夠稍微填補(bǔ)學(xué)習(xí)領(lǐng)域的空白。系列文章只會(huì)節(jié)選關(guān)鍵代碼輔以思路講解,請(qǐng)自行配合源碼閱讀。 作者:bromine鏈接:https://www.jianshu.com/p/2f6...來(lái)源:簡(jiǎn)書(shū)著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。Swoft...

    qpwoeiru96 評(píng)論0 收藏0
  • 后端知識(shí)拓展 - 收藏集 - 掘金

    摘要:阻塞,非阻塞首先,阻塞這個(gè)詞來(lái)自操作系統(tǒng)的線(xiàn)程進(jìn)程的狀態(tài)模型網(wǎng)絡(luò)爬蟲(chóng)基本原理一后端掘金網(wǎng)絡(luò)爬蟲(chóng)是捜索引擎抓取系統(tǒng)的重要組成部分。每門(mén)主要編程語(yǔ)言現(xiàn)未來(lái)已到后端掘金使用和在相同環(huán)境各加載多張小圖片,性能相差一倍。 2016 年度小結(jié)(服務(wù)器端方向)| 掘金技術(shù)征文 - 后端 - 掘金今年年初我花了三個(gè)月的業(yè)余時(shí)間用 Laravel 開(kāi)發(fā)了一個(gè)項(xiàng)目,在此之前,除了去年換工作準(zhǔn)備面試時(shí),我并...

    CoderBear 評(píng)論0 收藏0
  • 后端知識(shí)拓展 - 收藏集 - 掘金

    摘要:阻塞,非阻塞首先,阻塞這個(gè)詞來(lái)自操作系統(tǒng)的線(xiàn)程進(jìn)程的狀態(tài)模型網(wǎng)絡(luò)爬蟲(chóng)基本原理一后端掘金網(wǎng)絡(luò)爬蟲(chóng)是捜索引擎抓取系統(tǒng)的重要組成部分。每門(mén)主要編程語(yǔ)言現(xiàn)未來(lái)已到后端掘金使用和在相同環(huán)境各加載多張小圖片,性能相差一倍。 2016 年度小結(jié)(服務(wù)器端方向)| 掘金技術(shù)征文 - 后端 - 掘金今年年初我花了三個(gè)月的業(yè)余時(shí)間用 Laravel 開(kāi)發(fā)了一個(gè)項(xiàng)目,在此之前,除了去年換工作準(zhǔn)備面試時(shí),我并...

    Carl 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<