路由是外界访问Laravel应用程序的通路。那服务提供者顾名思义就是提供服务的。一般我们都是在 Routes 文件下面的路由文件中定义路由访问方法,路由访问方法定义到我们的识别的过程,肯定 是需要路由组件进行相关的处理,那这个组件是怎么被装入到容量里面的呢? 这个就是由服务提供者来完成的。
router这个服务是在实例化应用程序Application时在构造方法里通过注册 RoutingServiceProvider 时绑定到服务容器 里的:
public function __construct($basePath = null) { if ($basePath) { $this->setBasePath($basePath); } $this->registerBaseBindings(); //注册服务提供者 $this->registerBaseServiceProviders(); $this->registerCoreContainerAliases(); } protected function registerBaseServiceProviders() { $this->register(new EventServiceProvider($this)); $this->register(new LogServiceProvider($this)); //注册路由服务提供者 $this->register(new RoutingServiceProvider($this)); }这个方法就是注册路由,我们点进去就可以看到
/** * Register the router instance. * * @return void */ protected function registerRouter() { $this->app->singleton('router', function ($app) { return new Router($app['events'], $app); }); }这是注册一个Router实例,调用的静态方法都对应于\Illuminate\Routing\Router类里的方法,这个类里 包含了与路由的注册、寻址、调度相关的方法。 既然路由的实例我们找到了,那么路由文件是怎么注册到容器中呢? 在\Illuminate\Routing\RoutingServiceProvider::register() 类中往容器对象的 $bindings 数组属性 以 keyvalue 形式注册进去,然后就会由服务提供者进行相关服务的注册,并通过引导服务( \Illuminate\Foundation\Bootstrap\BootProviders::class)下面的 Boot 方法来引导每个服务的执行操作 调用 \App\Providers\RouteServiceProvider::boot() 方法加载路由列表。在这当中会调用父类的boot方 法,获取到先去容器里面的Router的注册类 laravel 首先去寻找路由的缓存文件,没有缓存文件再去进行加载路由。
public function boot() { $this->setRootControllerNamespace(); if ($this->routesAreCached()) { $this->loadCachedRoutes(); } else { $this->loadRoutes();-----》加载路由 $this->app->booted(function () { $this->app['router']->getRoutes()->refreshNameLookups(); $this->app['router']->getRoutes()->refreshActionLookups(); }); } } //加载 \App\Providers\RouteServiceProvider中的map方法 protected function loadRoutes() { if (method_exists($this, 'map')) { $this->app->call([$this, 'map']); } }调用 \App\Providers\RouteServiceProvider的map方法 通过map方法我们能看到 laravel将路由分为两个大组:api、web。这两个部分的路由分别写在两个文件中:routes/web.php、 routes/api.php。
通常都是用Route这个Facade调用静态方法get, post, head, options, put, patch, delete…等来注册路由。而Router这个实例我们第一步的时候就已经注册好了。
public function get($uri, $action = null) { return $this->addRoute(['GET', 'HEAD'], $uri, $action); } public function post($uri, $action = null) { return $this->addRoute('POST', $uri, $action); }其实这些方法都是调用addRoute这个方法
public function addRoute($methods, $uri, $action) { return $this->routes->add($this->createRoute($methods, $uri, $action)); }再里面首先要先“createRoute”之后再“add”
protected function createRoute($methods, $uri, $action) { //判断当前路由是不是控制器的方法 if ($this->actionReferencesController($action)) { //controller@action类型的路由在这类转换 $action = $this->convertToControllerAction($action); } $route = $this->newRoute( $methods, $this->prefix($uri), $action ); // 合并路由分组 if ($this->hasGroupStack()) { $this->mergeGroupAttributesIntoRoute($route); } //添加路由条件 $this->addWhereClausesToRoute($route); return $route; }在createRoute方法里面转换了形式 经过转换之后,我们的路由变成这样的形式
[ 'uses' => 'App\Http\Controllers\HomeController@index', 'controller' => 'App\Http\Controllers\HomeController@index' ]再看add方法
public function add(Route $route) { //添加到集合 $this->addToCollections($route); //添加到查找表中 $this->addLookups($route); return $route; } protected function addToCollections($route) { $domainAndUri = $route->getDomain().$route->uri(); foreach ($route->methods() as $method) { $this->routes[$method][$domainAndUri] = $route; } $this->allRoutes[$method.$domainAndUri] = $route; } protected function addLookups($route) { if ($name = $route->getName()) { $this->nameList[$name] = $route; } $action = $route->getAction(); if (isset($action['controller'])) { $this->addToActionList($action, $route); } }经过这步操作,我们的路由变成了这种,放在下面4个属性表里面——routes、allRoutes、nameList、actionList routes中存放了HTTP请求方法与路由对象的映射:
[ 'GET' => [ $routeUri1 => $routeObj1 ... ] ... ]allRoutes属性里存放的内容时将routes属性里的二位数组变成一位数组后的内容:
[ 'GET' . $routeUri1 => $routeObj1 'GET' . $routeUri2 => $routeObj2 ... ]nameList是路由名称与路由对象的一个映射表
[ $routeName1 => $routeObj1 ... ]actionList是路由控制器方法字符串与路由对象的映射表
[ 'App\Http\Controllers\ControllerOne@ActionOne' => $routeObj1 ]至此路由的注册就已经完成,下面就是最后一步,匹配相应的路由
路由查找是由HTTP请求在经过Pipeline通道上的中间件的前置操作后到达目的地: 这个方法就是请求经过一些中间件的验证,执行dispatchToRouter()方法。 最终路由的匹配就是在这个函数中完成的。点进去,发现先findRoute(),之后runRoute()
protected function findRoute($request) { $this->current = $route = $this->routes->match($request); $this->container->instance(Route::class, $route); return $route; } protected function runRoute(Request $request, Route $route) { $request->setRouteResolver(function () use ($route) { return $route; }); $this->events->dispatch(new Events\RouteMatched($route, $request)); return $this->prepareResponse($request, $this->runRouteWithinStack($route, $request) ); }这里面要经过一些验证,验证成功之后需routes中存放了HTTP请求方法与路由对象的映射。
当路由通过了全部的认证就将会被返回,接下来就要将请求对象URI里的路径参数绑定赋值给路由参数
public function bind(Request $request) { $this->compileRoute(); $this->parameters = (new RouteParameterBinder($this)) ->parameters($request); $this->originalParameters = $this->parameters; return $this; }经过绑定之后,紧接着就时候运行通过匹配路由中对应的控制器方法并返回响应对 象了 路由的寻址过程就结束了。 简化的整体流程图
