内容简介:我们直接从下面是内核引导启动函数接着就是把这一组类传给
本章将通过分析加载流程深入理解系统内置和 app 中配置的 provider 是如何加载的
我们直接从 Kernel 的 handle() 方法中开始分析, handle() 负责处理了请求,所有框架的启动也是在这里开始!
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());
}
复制代码
下面是内核引导启动函数
$this->bootstrappers() = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class, //找到了,它就是传给 App 对象进行启动的!
\Illuminate\Foundation\Bootstrap\BootProviders::class,
]
public function bootstrap()
{
if (! $this->app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers());
}
}
复制代码
接着就是把这一组类传给 Application 对象的 bootstrapWith() 方法,继续下去
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) 方法
我们只追踪 \Illuminate\Foundation\Bootstrap\RegisterProviders::class 这个类,看看它的 bootstrap($this) 方法
public function bootstrap(Application $app)
{
$app->registerConfiguredProviders();
}
复制代码
继续看 Application 的 registerConfiguredProviders() 方法
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 框架默认的加载的服务提供者
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
Illuminate\Cookie\CookieServiceProvider::class,
Illuminate\Database\DatabaseServiceProvider::class,
Illuminate\Encryption\EncryptionServiceProvider::class,
Illuminate\Filesystem\FilesystemServiceProvider::class,
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
Illuminate\Hashing\HashServiceProvider::class,
Illuminate\Mail\MailServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Pipeline\PipelineServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class,
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
/*
* Package Service Providers...
*/
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
],
复制代码
通过集合来操作,按照命名空间来分区,这里目前只有两组 Illuminate 和 App 开头的类
继续分析 $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);
[$this->make(PackageManifest::class)->providers()] 这段代码是 Laravel 5.5 引入的包自动发现功能,
主要实现了从所有的包的 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)
延迟加载的实现逻辑
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 【Laravel-海贼王系列】第十四章,Session 解析
- 【Laravel-海贼王系列】第十二章,Facade 模式解析
- 【Laravel-海贼王系列】第十章,Job&队列存储端实现
- 【Laravel-海贼王系列】第十三章,路由&控制器解析
- 【Laravel-海贼王系列】第十七章,Laravel 那些骚操作
- 慢雾安全 海贼王:从DApp亡灵军团,细说区块链安全
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First Python
Paul Barry / O'Reilly Media / 2010-11-30 / USD 49.99
Are you keen to add Python to your programming skills? Learn quickly and have some fun at the same time with Head First Python. This book takes you beyond typical how-to manuals with engaging images, ......一起来看看 《Head First Python》 这本书的介绍吧!