心血来潮发现laravel有php artisan serve监听端口的启动方式,突然想仔细看看laravel源码,本系列随时可能鸽,laravel版本8.83.27。为什么是这版本呢,因为在写这篇文章时直接执行laravel的composer方式安装默认就是这版本
切入口就基于根目录的artisan文件
常见的框架初始化入口,接下来去到/bootstrap/app.php
读注释可以理解到14~16行代码new了一个Illuminate\Foundation\Application实例,该实例作用是粘合laravel各组件和作为系统的依赖注入容器。
先关注Illuminate\Foundation\Application类的定义
Illuminate\Foundation\Application继承了Illuminate\Container\Container,作为依赖注入容器需要的功能基本都在Container里,其余实现的接口我们在后面用到时再讨论
来到Illuminate\Foundation\Application的构造方法
从这里开始我们把构造方法里的每一个调用拆分开解读
1.setBasePath($basePath)
2.registerBaseBindings()
3.registerBaseServiceProviders()
4.registerCoreContainerAliases()
这一步的主要作用是注册项目的目录结构,包括
base、lang、config、public、storage、database、resources、bootstrap
xxxPath()方法的内容都大致一样,我们随便挑一个$this->configPath()看看内容
该方法返回了项目根目录路径+文件分隔符+config+$path,假设项目根目录路径是/data/web/project,则返回结果就是
/data/web/project/config/$path
注意在构造方法流程里$path值为空,所以实际结果为 /data/web/project/config
接下来我们来看$this->instance()做了什么事,这里需要稍微注意下图5的调用里传的参数值格式 'path.xxx'
此时方法调用来到了Illuminate/Container/Container,由此方法注释可知这方法是Laravel的IOC容器对外开放的注册实例的入口(之一)。
instance()方法逻辑大致如下
1.清除已注册过的组件对应实例关系
2.注册实例
3.如果发现在本次注册实例之前已注册过,则重新执行注册回调函数
(reboundClassbacks可以由Illuminate/Container/Container::rebinding()写入)
要注意的是在setBasePath()这一步中并没有实例化任何类,只是在容器中注册了'path.xxx',与其对应的值是各目录路径字符串
至此setBasePath()的逻辑结束。
这一步的逻辑是
把Illuminate\Foundation\Application自身注册为Laravel的IOC容器实例
把自身注册为'app'的实例、把自身注册为Illuminate\Container\Container的实例
注册Illuminate\Foundation\Mix单例到容器
注册Illuminate\Foundation\PackageManifest单例到容器
Mix是关于前端资源管理的类,PackageManifest是关于项目的包发现类(感兴趣的可以去看vendor/laravel/framework/src/Illuminate/Foundation/PackageManifest.php的bind()方法),此处不做这两个类的分析。
至此registerBaseBindings()的逻辑结束。
这一步就如方法名字面上的意思,注册基础Provider
注意到这几个Provider都继承了抽象类Illuminate\Support\ServiceProvider,这个类允许子类重写register()方法,而这些Provider的构造函数都是基于Illuminate\Support\ServiceProvider的构造函数
先来看Illuminate/Foundation/Application的register()方法
$this->getProvider($provider) 的逻辑是读取 $this->serviceProviders$provider::class ,返回值是null或具体的Provider实例,当Provider已注册过且$force为false时不重复注册
$this-> resolveProvider($provider) 的逻辑是简单的new一个对应Provider实例
接着是调用Provider的register()方法,后续我们会分别来看这三个Provider的register()方法
687~697行为Provider提供了方便的依赖注入方式,在前面的内容中可以了解到Provider在构造函数中传入了Illuminate/Foundation/Application,意味着开发者可以在Provider内通过$this->app去获取bindings和singletons中的实例
后续可能会单开个篇幅来看bind()和singleton()的实现,目前只需要了解它会关联到IOC容器注册且实例化类的核心,且在第一次显示获取实例前不会实例化对应类,即是注册时并不会实例化类或执行注册的回调函数,不了解反射的请先学习一下
$this->markAsRegistered($provider)的逻辑很简单,存入Provider实例到$this->serviceProviders,标记Provider已注册
由于此时$this->isBooted()的值默认为false ,先不关注bootProvider()的逻辑
至此register()方法的逻辑结束,来看看三个Provider的register()方法有什么逻辑
注册events单例组件Illuminate\Events\Dispatcher,而Illuminate\Events\Dispatcher内又引用了Illuminate\Contracts\Queue\Factory接口
我们能在Illuminate/Foundation/Application的构造方法第四步registerCoreContainerAliases()中得知Illuminate\Contracts\Queue\Factory接口绑定的具体实现是Illuminate\Queue\QueueManager
注册log组件Illuminate\Log\LogManager
这里就不一一截图代码出来了,简述一下各个方法的内容
$this->registerRouter() 注册 router 单例组件 Illuminate\Routing\Router
路由核心组件,功能包括注册路由、重定向、匹配请求路由(dispatch)、响应请求(toResponse)等
$this->registerUrlGenerator() 注册 url 单例组件 Illuminate\Routing\UrlGenerator
可以视作 Illuminate\Routing\Router 的装饰器,功能包括多种格式化路由Url的方式和router部分API
$this->registerRedirector() 注册 redirect 单例组件 Illuminate\Routing\Redirector
负责请求重定向,注入了Illuminate\Routing\UrlGenerator
$this->registerPsrRequest() 注册 Psr\Http\Message\ServerRequestInterface 组件 Nyholm\Psr7\Factory\Psr17Factory
这个包默认没有下载,需要手动执行composer require symfony/psr-http-message-bridge,symfony的基于psr7的请求工厂类,详情请看文档
$this->registerPsrResponse() 注册 Psr\Http\Message\ResponseInterface 组件 Nyholm\Psr7\Response
这个包默认没有下载,需要手动执行composer require nyholm/psr7,轻量级psr7实现,详情请看github
$this->registerResponseFactory() 注册 Illuminate\Contracts\Routing\ResponseFactory 单例组件 Illuminate\Routing\ResponseFactory
响应工厂类,负责返回多种格式的响应类,如 Illuminate\Http\JsonResponse、Symfony\Component\HttpFoundation\StreamedResponse 等
$this->registerControllerDispatcher() 注册 Illuminate\Routing\Contracts\ControllerDispatcher 单例组件 Illuminate\Routing\ControllerDispatcher
控制器调度类,实现了dispatch()和getMiddleware()方法供router发起调度执行对应逻辑
至此registerBaseServiceProviders()的逻辑结束
这个方法内注册了大量组件alias,大部分组件在这个时候还没有被实例化,仅注册了类似于实现类与interface的“别名”关系
具体的alias()方法实现会和bind()和singleton()一起看,到时候可能会基于这里挑个例子来讲流程并验证结果
至此registerCoreContainerAliases()的逻辑结束
这次内容只看了Application的实例化,做的事情不多,可以概括为基于IoC容器,组织项目目录结构,注册Application作为IoC容器实例,注册路由、Event、日志三大模块的Provider,注册基础组件(如db、redis、queue)的接口及实现关系。
最后放个简单的图
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。