3.1.0 - Laravel - 5.6 - Route - RoutingServiceProvider注册
RoutingServiceProvider对路由来说是第一个需要加载的服务。提供了很多必要的绑定。
加载路径步骤如下
index.php 引入
app.php
require_once __DIR__.'/../bootstrap/app.php';
bootstrap/app.php
文件中会创建容器对象Application
$app = new Illuminate\Foundation\Application( realpath(__DIR__.'/../') );
在namespace Illuminate\Foundation\Application 下的Apllication的构造函数中会调用registerBaseServiceProviders方法。
public function __construct($basePath = null) { ... $this->registerBaseServiceProviders(); ... }
registerBaseServiceProviders方法如下,会调用register方法注册RoutingServiceProvider方法。
调用register方法会去执行这个RoutingServiceProvider中的register方法中的逻辑。可以把需要的逻辑比如绑定一些必要的对象等等写在这个register方法中。
(register方法的逻辑参考上一篇 application的register)
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));
$this->register(new LogServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
}
- 主要看下 RoutingServiceProvider 的 register方法, 内容如下。
public function register() { $this->registerRouter(); $this->registerUrlGenerator(); $this->registerRedirector(); $this->registerPsrRequest(); $this->registerPsrResponse(); $this->registerResponseFactory(); $this->registerControllerDispatcher(); }
5.1 $this->registerRouter();
这个方法就是通过singleton方法注册router实例到容器中。(router对象提供很多必要的方法帮助我们操作后面会生成的route对象)。代码如下。
protected function registerRouter()
{
$this->app->singleton('router', function ($app) {
return new Router($app['events'], $app);
});
}
很简单,就是绑定一个闭包,闭包会创建router对象返回。
5.2 $this->registerUrlGenerator();
这一步就是绑定url和其产生UrlGenerator对象的回调方法。就是注册一个url产生器。
protected function registerUrlGenerator()
{
$this->app->singleton('url', function ($app) {
$routes = $app['router']->getRoutes();
$app->instance('routes', $routes);
return new UrlGenerator($routes, $app->rebinding(
'request',
$this->requestRebinder()),
$app['config']['app.asset_url']
);
});
$this->app->extend('url', function (UrlGeneratorContract $url, $app) {
$url->setSessionResolver(function () {
return $this->app['session'] ?? null;});
$url->setKeyResolver(function () {
return $this->app->make('config')->get('app.key');
});
$app->rebinding('routes', function ($app, $routes) {
$app['url']->setRoutes($routes);
});
return $url;
});
}
这里主要分两步,分别使用singleton和extend方法绑定字段名为url
对应的闭包。
singleton会把
url
和其闭包绑定。但是不会执行闭包
,要在make解析的时候才执行,执行完会存入instance数组中。
extend绑定对前面这个url
绑定的扩展。如果url
已经解析过了就会把extend返回值替换instance中的值,如果没有就会把方法存入extender数组以备后用。简单说就是extend是对singleton的url的扩展。
1.1 $routes = $app['router']->getRoutes();
获取router对象的所有routes对象。(route对象就是我们产生的一个一个路由对象,用来和请求request匹配)
1.2 把route对象集合以名字routes
存入instance数组中。
1.2 返回一个UrlGenerator对象。可以生成url的对象。
1.2.1 $app->rebinding('request', $this->requestRebinder()
requestRebinder方法返回一个设置request到$app['url']
的闭包。代码如下
protected function requestRebinder()
{
return function ($app, $request) {
$app['url']->setRequest($request);
};
}
rebinding通常对已经解析过的abstruct,重新绑定的时候触发回调函数。
但是他还有个特性,如果当前abstruct以前被绑定过,他会马上解析当前的abstruct。
(abstruct这里就是’url’)。
这里就是,如果request被绑定过就会马上解析这个request对象。
整句的意思就是如果request被绑定过,就会马上解析request返回,同时还把rebinding的回调函数存入reboundCallbacks数组中等待再次绑定的时候触发。很巧妙。
1.2.2 $app['config']['app.asset_url']
是asset静态文件路径url
2.1 第二个extend绑定,返回一个方法,触发的时候需要两个参数:app和UrlGeneratorContract的实现类(就是上面的UrlGenerator对象)。
2.2 设置一些必要的特性以便以后使用
$url->setSessionResolver(function () {
return $this->app['session'] ?? null;
});
$url->setKeyResolver(function () {
return $this->app->make('config')->get('app.key');
});
这里设置了一些回调函数给UrlGenerator对象,到时候可以触发获取一些信息,比如session以及配置文件中的app.key的值。
2.3 和前面同理。rebinding会对routes马上解析,因为上面已经使用instances绑定过routes了。返回routes
解析后的对象。同时会把rebinding回调函数存入以备后用。
$app->rebinding('routes', function ($app, $routes) {
$app['url']->setRoutes($routes);
});
最后返回这个url对象。
前面提及,extend有替换的作用,前提是这个绑定已经被解析过。在这里,singleton绑定
url
和extend绑定url
之间没有解析的过程,所以就没有替换的情况,他会依次执行,先解析singleton然后触发extend的回调,相当于给url对象做了一个辅助扩展。
5.3 $this->registerRedirector();
注册一个重定向对象。
protected function registerRedirector()
{
$this->app->singleton('redirect', function ($app) {
$redirector = new Redirector($app['url']);
if (isset($app['session.store'])) {
$redirector->setSession($app['session.store']);
}
return $redirector;
});
}
1.就是绑定redirect
和其回调函数。
2.判断$app['session.store']
是否存在,存在把其存入redirect对象中。
session.store 是Session驱动器,是Illuminate\Session\Store的实例,Store类实现了Illuminate\Contracts\Session\Session向开发者提供了统一的接口来访问Session数据,驱动器通过不同的SessionHandler来访问database、redis、memcache等不同的存储介质里的session数据。
5.4 registerPsrRequest和registerPsrResponse放一起。绑定满足psr-7标准的request和response。但是不知道具体如何使用。代码中无迹可寻。请大牛指点。
protected function registerPsrRequest()
{
$this->app->bind(ServerRequestInterface::class, function ($app) {
return (new DiactorosFactory)->createRequest($app->make('request'));
});
}
protected function registerPsrResponse()
{
$this->app->bind(ResponseInterface::class, function () {
return new PsrResponse;
});
}
5.5 注册响应的工厂对象,主要用来生成返回给用户的数据。
protected function registerResponseFactory()
{
$this->app->singleton(ResponseFactoryContract::class, function ($app) {
return new ResponseFactory($app[ViewFactoryContract::class], $app['redirect']);
});
}
5.6 这个是注册控制器的分发对象。ControllerDispatcher这个对象主要用来触发request对应的controller逻辑。
protected function registerControllerDispatcher()
{
$this->app->singleton(ControllerDispatcherContract::class, function ($app) {
return new ControllerDispatcher($app);
});
}
5.5 5.6没有详细解释因为不在本章范围,后面遇到操作再补充。
这就是整个RoutingServiceProvider加载过程。
总结:
1.RoutingServiceProvider的加载不复杂,主要是要明确他是在容器初始化的时候加载的,比其他一些serviceprovider要早。
2.连续使用singleton和extend 可以避免extend绑定的替换特性。
3.RoutingServiceProvider的加载的作用主要还是绑定各个回调函数,还并没有真正触发这些回调。在给后面的操作打下基础。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: