开始
我们直接从
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,
// "找到了,它就是传给 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()方法,继续下去
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' => [
Illuminate\Auth\AuthServiceProvider::class,
... 省略类似代码
Illuminate\View\ViewServiceProvider::class,
App\Providers\AppServiceProvider::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)
延迟加载的实现逻辑