Skip to main content

路由的加载

简介

上一章,我们说过,在 Laravel 启动引导中,BootProviders 类的根本,就是调用 app.php 配置文件中服务类的 boot 方法。这一章我们就 App\Providers\RouteServiceProvider 这个服务类的 boot 方法进行解析。为什么要重点说这个类的 boot 方法呢,因为这个 boot 方法,就对我们在 web.phpapi.php 中路由进行解析,并存入 Route 类服务实体中,最终绑定到 Application 容器实体中,为请求 URL 进行控制器和方法匹配,从而执行我们定义的方法。

正文

先看一下 App\Providers\RouteServiceProviderboot 方法

public function boot()
{
//

parent::boot();
}

发现调用了父类的 boot 方法,那么我们去看父类

public function boot()
{
// 这一行,从词面不难看出是在设置根控制器命名空间,真实操作是设置 UrlGenerator 类对象中 rootNamespace 属性的值
$this->setRootControllerNamespace();

// 这一行是看 `./bootstrap/cache/routes.php` 缓存文件存在吗,存在的话就直接加载
if ($this->app->routesAreCached()) {
$this->loadCachedRoutes();
} else {
// **这个是路由加载的核心方法
$this->loadRoutes();

// 这一行,还记得上一中的 booted 的,就是全部服务类加载完后的操作;这里就是定义加载完后要执行的回调函数
$this->app->booted(function () {
$this->app['router']->getRoutes()->refreshNameLookups();
$this->app['router']->getRoutes()->refreshActionLookups();
});
}
}

我们接着看 $this->loadRoutes() 这个核心方法

protected function loadRoutes()
{
if (method_exists($this, 'map')) {
$this->app->call([$this, 'map']);
}
}

这段代码,从字面不难看出,就是判断当前对象有没有 map 方法,有的话就执行。注意,当前对象是 App\Providers\RouteServiceProvider 实例化的,所以我们要从这个服务类开看起,不应该直接去看它的父类。经过寻找,map 方法被发现

public function map()
{
$this->mapApiRoutes();

$this->mapWebRoutes();

//
}

哇,好熟悉,这不就是我们可以自行修改或添加自定义路由文件的方法吗。第一行加载 api.php ,第二行加载 web.php

我们以 api.php 来看

protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}

执行顺序:调用 Route 门面,从 route 解析出 Route 服务实体,然后调用 prefix 方法,这个方法实际是在 Route 实体对象 attributes 属性中设定 prefix => 'api' 数组键值对。然后调用 middleware 方法也是, namespace 方法也是,我们看一下结果图

file

最后 group 方法,会携带上面的 attributes 属性去执行 web.php 文件,如下

protected function loadRoutes($routes)
{ // $routes = 'D:/www/learn/routes/api.php'
if ($routes instanceof Closure) {
$routes($this);
} else {
$router = $this;

require $routes;
}
}

会包含执行 api.php,将路由绑定的 Route 服务实体中。