咱們直接從
Kernel
的handle()
方法中開始分析,handle()
負責處理了請求,全部框架的啓動也是在這裏開始!php
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());
}
複製代碼
下面是內核引導啓動函數laravel
$this->bootstrappers() = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
// "找到了,它就是傳給 App 對象進行啓動的!"
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
// "這是在全部 provider 註冊以後調用他們的 boot() 方法"
\Illuminate\Foundation\Bootstrap\BootProviders::class,
]
public function bootstrap()
{
if (! $this->app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers());
}
}
複製代碼
接着就是把這一組類傳給
Application
對象的bootstrapWith()
方法,繼續下去json
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)
方法bootstrap
只需追蹤
\Illuminate\Foundation\Bootstrap\RegisterProviders::class
的bootstrap($this)
方法緩存
public function bootstrap(Application $app)
{
$app->registerConfiguredProviders();
}
複製代碼
繼續看
Application
的registerConfiguredProviders()
bash
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
框架默認的加載的服務提供者app
'providers' => [
Illuminate\Auth\AuthServiceProvider::class,
... 省略相似代碼
Illuminate\View\ViewServiceProvider::class,
App\Providers\AppServiceProvider::class,
... 省略相似代碼
App\Providers\RouteServiceProvider::class,
],
複製代碼
經過集合來操做,按照命名空間來分區,這裏目前只有兩組
Illuminate
和App
開頭的類composer
繼續分析
$providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);
框架
[$this->make(PackageManifest::class)->providers()]
這段代碼是 Laravel 5.5
引入的包自動發現功能,ide
主要實現了從全部的包的 composer.json
文件下讀取 laravel
下的 providers
中的內容。
在引入這個功能後不少包都不須要咱們手動再去 app.providers
中去配置了。
在這行代碼執行完成以後 $providers
會變成一個分紅了三段的集合對象。
繼續看下去
(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
的樣子
繼續看下一段,若是傳入的 $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
的服務提供者功能是如何運做的就分解完了
其中關於 Filesystem
和 ProviderRepository
對象的功能沒有詳細分解,這裏東西並很少,不展開了!
還有就是 PackageManifest::class
這個對象的功能主要是從
composer.json
的交互以及構建 bootstrap/cache
下面的緩存文件。
能夠梳理一下思路:
框架是怎麼加載的 (優先緩存文件,對比更新)
provider 提供了那些功能(延遲加載,事件監聽)...
包自動發現的實現! (經過讀取composer.json)
延遲加載的實現邏輯