xiaohuilam / laravel

Laravel 深入详解 —— 源代码解析,新手进阶指南
433 stars 80 forks source link

06. RouteServiceProvider 详解 #6

Open xiaohuilam opened 5 years ago

xiaohuilam commented 5 years ago

RouteServiceProvider 详解

RouteServiceProvider::boot 阶段,所有路由都只是执行登记,匹配的逻辑是在 02. HTTP Kernel Handle解析 的 dispatchToRouter 阶段。

我们先找到 RouteServiceProvider.php

config/app.phpproviders 中定义的 RouteServiceProvider 其实是: https://github.com/xiaohuilam/laravel/blob/6d9215c0a48aa68ef65d83c3ab8d1ba3c4e23d39/config/app.php#L161 而这个类其实继承自 Illuminate\Foundation\Support\Providers\RouteServiceProvider https://github.com/xiaohuilam/laravel/blob/6d9215c0a48aa68ef65d83c3ab8d1ba3c4e23d39/app/Providers/RouteServiceProvider.php#L6-L9

我们分析 Illuminate\Foundation\Support\Providers\RouteServiceProvider 的代码

register() 阶段

register 方法是空的 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Support/Providers/RouteServiceProvider.php#L81-L89

boot() 阶段

而在 boot 阶段,几乎运行了 RouteServiceProvider 中的所有方法 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Support/Providers/RouteServiceProvider.php#L24-L43

一. setRootControllerNamespace()

https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Support/Providers/RouteServiceProvider.php#L45-L55

二. 判断路由是否缓存过

Illuminate\Foundation\Application::routesAreCached, 其实就是判断文件 bootstrap/cache/routes.php 是否存在 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Application.php#L895-L903 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Application.php#L905-L913 存在则直接加载 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Support/Providers/RouteServiceProvider.php#L57-L67

三. 如果没有缓存,则遍历路由

https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Foundation/Support/Providers/RouteServiceProvider.php#L69-L79 map 方法在这里 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/app/Providers/RouteServiceProvider.php#L31-L43 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/app/Providers/RouteServiceProvider.php#L45-L72 这里的 mapWebRoutesmapApiRoutes 是分别将 routes/web.php 和 routes/api.php 用 Route 门面类的 group 加载了一遍。 Route::group 实质运行到的是 Illuminate\Routing\Router::group (关于门面类的文章请见 [TODO]),代码为 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L356-L416 上面的逻辑是一层层剥开 Route::group 去执行里面的 Route::get / Route::post ... https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L133-L214 不难发现,不管是 GET/POST... 还是 any,都只是调用了 addRoute 将这个路由的属性登记了一下: https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L434-L445 $this->routes 是一个 \Illuminate\Routing\RouteCollection 的集合类,add 由这个方法生成的路由 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L447-L478

四. dispatch() -> runRoute() 阶段

路由登记完成后,就是 Kernel 触发管道层层剥洋葱调用中间件最后触发路由 dispatch (当然,这已经运行到 RouteServiceProvider 的外面了)。

剥洋葱的过程请查阅 05. Pipeline 解析

https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Router.php#L660-L682

第679行的 $route->run 是至关重要的,调用到了 controller (本质其实是使用 09. 容器的依赖注入机制 将路由方法所依赖参数解析出来,并运行) https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Routing/Route.php#L158-L176