今天这篇博文来探索一下laravel的路由。在第一篇讲laravel入口文件的博文里,我们就提到过laravel的路由是在application对象的初始化阶段,通过provider来加载的。这个路由服务提供者注册于vendor\laravel\framework\src\Illuminate\Foundation\Application.php的registerBaseServiceProviders方法
protected function registerBaseServiceProviders() { $this->register(new EventServiceProvider($this)); $this->register(new LogServiceProvider($this)); $this->register(new RoutingServiceProvider($this)); }
可以看到这个方法对路由provider进行了注册,我们最开始的博文也提到过,这个register方法实际上是运行了provider内部的register方法,现在来看一下这个provider都提供了些什么vendor\laravel\framework\src\Illuminate\Routing\RoutingServiceProvider.php
public function register() { $this->registerRouter(); $this->registerUrlGenerator(); $this->registerRedirector(); $this->registerPsrRequest(); $this->registerPsrResponse(); $this->registerResponseFactory(); $this->registerControllerDispatcher(); }
这个服务提供者类中将许多对象都添加到了laravel的容器中,其中最重要的就是第一个注册的Router类了。Router中包含了我们写在路由文件中的get、post等各种方法,我们在路由文件中所使用的Route::any()方法也是一个门面类,它所代理的对象便是Router。
看过了路由的初始化,再来看一下我们在路由文件中所书写的路由是在什么时候加载到系统中的。在config/app.php文件中的privders数组中有一个名为RouteServiceProvider的服务提供者会跟随laravel系统在加载配置的时候一起加载。这个文件位于\app\Providers\RouteServiceProvider.php刚刚的Routing对路由服务进行了注册,这里的RouteServiceProvider就要通过刚刚加载的系统类来加载我们写在routes路由文件夹中的路由了。
至于这个provider是何时开始启动的,还记得我们第一篇博客中介绍的Illuminate\Foundation\Bootstrap\BootProviders这个provider吗?这个provider在注册时便会将已经注册过的provider,通过application中的boot方法,转发到它们自身的boot方法来启动了。
而RouteServiceProvider这个类的boot方法通过它父类boot方法绕了一圈后又运行了自己的mapWebRoutes方法。
//Illuminate\Foundation\Support\Providers\RouteServiceProvider.php public function boot() { //设置路由中控制器的命名空间 $this->setRootControllerNamespace(); //若路由已有缓存则加载缓存 if ($this->app->routesAreCached()) { $this->loadCachedRoutes(); } else { //这个方法启动了子类中的map方法来加载路由 $this->loadRoutes(); $this->app->booted(function () { $this->app['router']->getRoutes()->refreshNameLookups(); $this->app['router']->getRoutes()->refreshActionLookups(); }); } } protected function loadRoutes() { if (method_exists($this, 'map')) { //这里又把视线拉回了子类,执行了子类中的map方法 $this->app->call([$this, 'map']); } }
这里这个mapWebRoutes方法有点绕,它先是通过门面类将Route变成了Router对象,接着又调用了Router中不存在的方法middleware,通过php的魔术方法__call将执行对象变成了RouteRegistrar对象(\Illuminate\Routing\RouteRegistrar.php)在第三句调用group方法时,又将路由文件的地址传入了Router方法的group方法中。
protected function mapWebRoutes() { //这里的route门面指向依旧是router,middleware方法通过__call重载将对象指向了RouteRegistrar对象 Route::middleware('web') //RouteRegistrar对象也加载了命名空间 ->namespace($this->namespace) //这里RouteRegistrar对象中的group方法又将对象方法指向了Router中的group方法 ->group(base_path('routes/web.php')); }
//Router类 public function __call($method, $parameters) { if (static::hasMacro($method)) { return $this->macroCall($method, $parameters); } //在这里通过重载实例化对象 if ($method == 'middleware') { return (new RouteRegistrar($this))->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters); } return (new RouteRegistrar($this))-&g