老司机带你深入 Laravel 之 ServiceProvider 原理

生活的感慨

生活中的朋友不需要太多,有那么几个就可以了,去关心那些真心关心你的人,至于其他人嘛,都是路人,不要把那些真心关心你的人,当成一坨屎,他们都是你最为宝贵的财富。温文尔雅,不卑不亢,无论是遇到漂亮的姑娘还是公司的领导,都要波澜不惊,自然优雅。

博文简介

在今天的这一篇博文中,我会带领大家深入的学习ServiceProvider,为什么要讲这个,我们来看官方的文档结构。

老司机带你深入Laravel之ServiceProvider

Laravel官方在架构理念这里把它列出来,所以ServiceProvider的重要性,就不言而喻了,但是,今天在这里我不打算讲Laravel的基础用法,如果你不清楚这块的内容,你可以参考官方文档,Laravel之ServiceProvider,我今天要讲的是,Laravel是如何引导和加载ServiceProvider的。

阅读基础

在阅读这篇博文之前,希望你已经知道如何使用ServiceProvider,这个我在上面讲过了。

一定要阅读我的上一篇博文,老司机带你深入理解 Laravel 之 Facade,这不是在开玩笑,我不想再讲我已经讲过的东西,希望你对自己负责,不然的话,我觉得你没有必要再阅读下面的内容了,浪费自己的时间,有这个时间还不如干点儿正事。

深入源代码

在类Illuminate\Foundation\Http\Kernel类的bootstrappers属性中,如下:
老司机带你深入Laravel之ServiceProvider

我们只关心\Illuminate\Foundation\Bootstrap\RegisterProviders::class这个类,在这里Laravel会加载所有的ServiceProvider,下面我们进入到它的bootstrap方法中:

老司机带你深入Laravel之ServiceProvider

可以看到它的bootstrap方法很简单,这里直接调用了Application类的registerConfiguredProviders方法,这个方法如下所示:

老司机带你深入Laravel之ServiceProvider

上面的代码你看不懂没关系,我仔细给你解释下,在这之前,我提醒大家,去看看Collection类的使用,估计很多人没使用过,真的很好用,$this->config['app.providers']就是你在config/app.php的providers属性中所定义的所有的ServiceProvider,Collection的partition方法会使用它的回调参数,重新生成一个只有2个元素的Collection对象,2个子元素都是Collection类的对象,第一个子元素包含的都是Laravel自身定义的ServiceProvider,第二个Collection包含了我们自己定义的ServiceProvider和第三方包定义的,这里的PackageManifest::class的providers方法会返回Composer包定义的,至于为啥是这样,建议你去看我在老司机带你深入理解 Laravel 之 Facade中的说明,不再陈述。Collection类的splice方法将其它包定义的provider加入到当前容器中。Application类的getCachedServicesPath方法返回缓存的路径,这个路径指的是啥呢?就是下面这个:

老司机带你深入Laravel之ServiceProvider

这是一个缓存文件,记录了当前系统所有的ServiceProvider,你一定要去看一下这个文件,文件的结构一目了然。
ProviderRepository这个类很简单,我们的ServiceProvider就是在这个类中进行加载的,下面我们来看它的load方法,它的参数就是系统所有的ServiceProvider。

老司机带你深入Laravel之ServiceProvider

首先调用loadManifest方法,这个方法很简单,它的作用就是加载项目根目录下面的bootstrap\cache\packages.php文件,就是我上面的截图,上面说了这是一个缓存文件,回到load方法中,继续调用shouldRecompile方法,这个方法会比较当前所有的ServiceProvider和之前缓存的ServiceProvider是否是相等的,如果相等,也就是说我们可以忽略编译的过程,这里我们假设缓存失效或者是不存在,那么compileManifest方法就会被调用,开始编译所有的ServiceProvider,下面是这个方法的代码:

老司机带你深入Laravel之ServiceProvider

首先调用freshManifest方法,freshManifest方法很简单,如下:

老司机带你深入Laravel之ServiceProvider

紧接着遍历所有的ServiceProvider,createProvider会创建对应ServiceProvider类的对象,在这里我们要提一句,就是Laravel中,所有的ServiceProvider都继承自Illuminate\Support\ServiceProvider类。Illuminate\Support\ServiceProvider类有一个方法isDeferred,表示当前的ServiceProvider是延迟的还是非延迟的,如果ServiceProvider的话,它的register方法不会马上被调用,相反,此时的ServiceProvider应该提供provides方法,我们打开bootstrap/cache/services.php文件可以看到,Laravel中存在很多的ServiceProvider是延迟的,截图如下:

老司机带你深入Laravel之ServiceProvider

打开Illuminate\Cache\CacheServiceProvider文件,它的provides方法如下:

老司机带你深入Laravel之ServiceProvider

所以如果以后,你从容器里面获取cache,cache.store或者是memcached.connector的时候,都会导致CacheServiceProvider的register方法被调用,这也就是延迟ServiceProvider的由来。回到compileManifest方法中,它会遍历provides方法的返回结果,并且把它存储到$manifest数组中,$manifest数组的格式和freshManifest方法返回的是一样的,上面的截图已经说明一切。$manifest['when']记录事件,这个我们很少用到,暂不分析它,继续往下走
,如果ServiceProvider不是延迟的,那么把它加入到$manifest['eager']数组中,最后调用writeManifest方法把$manifest写入到bootstrap/cache/services.php缓存文件中。好了,compileManifest方法分析完了,我们回到load方法中:

老司机带你深入Laravel之ServiceProvider

$manifest['eager']中存储的provider是需要立即被注册的,这里的Application类的register方法被调用,这个方法是专门用于注册ServiceProvider的,这个函数我已经在老司机带你深入理解 Laravel 之 Facade讲过了,大家不记得可以回去看一下,主要就是ServiceProvider的register方法被调用。回到load方法中,Application的addDeferredServices方法被调用,该方法很简单:

老司机带你深入Laravel之ServiceProvider

到这里为止,ServiceProvider的加载基本讲完了,但是你是否还有疑惑,就是延迟的ServiceProvider是什么时候加载的,下面我们来看一下,Application重写了Container的make方法:

老司机带你深入Laravel之ServiceProvider

这里首先判断需要加载的服务是不是属于延迟ServiceProvider的,如果是的话,loadDeferredProvider方法被调用,如下:

老司机带你深入Laravel之ServiceProvider

这个放调用到了registerDeferredProvider方法,如下:

老司机带你深入Laravel之ServiceProvider

这段代码是不是很熟悉,我就不再解释了。

如果你自己写过ServiceProvider的话,那么你可能会实现boot方法,这个方法是啥时候被调用的呢?还记得这张图么?
老司机带你深入Laravel之ServiceProvider

这里的BootProviders就是这里的关键,它的bootstrap方法如下:

老司机带你深入Laravel之ServiceProvider

Application的boot方法,如下:

老司机带你深入Laravel之ServiceProvider

boot方法调用bootProvide方法,如下:

老司机带你深入Laravel之ServiceProvider

这里检查如果你的ServiceProvider实现了boot方法,那么就会调用,到这里位置Laravel的ServiceProvider就讲解完毕了,如果你存在疑惑或者是需要帮助,可以加这个群:

总结

没有人天生就能够读懂复杂的代码,你需要的是耐心和对成功的渴望,不要给自己退缩的理由,你真的可以,我觉得你也应该这么做。

本作品采用《CC 协议》,转载必须注明作者和本文链接
微信:okayGoHome
本帖由系统于 4年前 自动加精
Dennis_Ritchie
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 9

支持 期待更多优质文章

4年前 评论

看完,内心好舒服

4年前 评论
lochpure

:+1:

4年前 评论

貌似在b站看到一个老哥的laravel视频

4年前 评论

每看一遍理解都有所加深

4年前 评论

细细看了一遍,写的很好

3年前 评论
gutao123

写的太棒了!

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!