3.1.1 - Laravel - 5.6 - Route - Application的Register方法源码分析
Application 提供了一个 register 方法用来注册 service provider。举例使用如下:
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));
$this->register(new LogServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
}
这个方法是 Application 初始化时注册 Serviceprovider 的方法。
最后一个 ServiceProvider 涉及到了 RoutingServiceProvider 的注册。这个 RoutingServiceProvider 对路由机制来说很重要,所以在分析它之前,先具体来看看 Application 的注册机制。方便后面阅读。
- 查看 application 中的 register 方法源码
public function register($provider, $force = false)
{
if (($registered = $this->getProvider($provider)) && ! $force) {
return $registered;
}
if (is_string($provider)) {
$provider = $this->resolveProvider($provider);
}
$provider->register();
if (property_exists($provider, 'bindings')) {
foreach ($provider->bindings as $key => $value) {
$this->bind($key, $value);
}
}
if (property_exists($provider, 'singletons')) {
foreach ($provider->singletons as $key => $value) {
$this->singleton($key, $value);
}
}
$this->markAsRegistered($provider);
if ($this->isBooted()) {
$this->bootProvider($provider);
}
return $provider;
}
1.1 第一步
if (($registered = $this->getProvider($provider)) && ! $force) {
return $registered;
}
1.1.1 主要就是调用 getProvider($provider)
。作用就是检查前面是否已经加载了我们需要的 provider,如果是,返回这个 provider,不用再加载。如果不是,就返回 null。
array_values: 返回数组的 value 值不包含 key
看下 getProvider()
方法源码
public function getProvider($provider)
{
return array_values($this->getProviders($provider))[0] ?? null;
}
$this->getProviders($provider))[0]
方法获取的数组的第一个值,说明返回的是一个数组。
这个方法没什么好说的就是调用方法 getProviders()
注意最后有个 s 和上面的
getProvider()
的区别。
1.1.1.1 我们看下 getProviders () 方法:
public function getProviders($provider)
{
$name = is_string($provider) ? $provider : get_class($provider);
return Arr::where($this->serviceProviders, function ($value) use ($name) {
return $value instanceof $name;
});
}
这个方法分两步,
a. 如果参数 $provider
是字符串,直接赋值给变量 $name
,如果不是,使用 get_class
获取该参数 $provider
所属类的路径,然后返回。
在我们当前的例子中,$provider
是 RoutingServiceProvider($this)
对象,所以返回的是类 RoutingServiceProvider
的类路径。
这里我们知道 provider 可以直接是一个类路径的字符串,联想到 serviceprovider 注册的另外一种形式是通过配置类路径来实现就很好理解了。
b. 然后调用了工具类 Arr 的 where 方法。
public static function where($array, callable $callback)
{
return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
}
where 方法的功能是使用第二个参数(回调函数)对第一个参数(数组)进行过滤查找,找出需要的值。
在这里:
I. 第一个参数 $array
是 $this->serviceProviders
数组
II.$this->serviceProviders
是前面所有已经注册了的 serviceprovider
所以就是在 $this->serviceProviders
这个存放前面已经注册的 service provider
中找到对应的 provider 返回。
总结:1.1#
现在我们知道了 $this->getProvider($provider))
得到的是在 serviceprovider 数组中获取已经存在的 provider。
那这句就是判断如果已经注册了需要的 provider,同时变量 force 是 false 的情况下(这个情况下 force 用来控制是否需要查找存在的 serviceprovider),直接返回存在的 $provider。
1.2 如果这个 provider 是一个 string,我们使用 `resolveProvider` 方法来处理
if (is_string($provider)) {
$provider = $this->resolveProvider($provider);
}
1.2.1 看下 resolveProvider 方法,很简单直接 new 一个 provider 对象返回。
public function resolveProvider($provider)
{
return new $provider($this);
}
1.3 第三步代码:
$provider->register();
这里 $provider
通过第二步,已经肯定是一个 provider 对象了。就直接调用该对象的 register 方法。
当前例子中就是 RoutingServiceProvider($this)
对象的 register 方法。
1.3.1 因为 RoutingServiceProvider 的实现逻辑不是本文的目的,所以简单看一下。
进入 RoutingServiceProvider 类的 register 方法。
方法如下,提供了几个方法。
public function register()
{
$this->registerRouter();
$this->registerUrlGenerator();
$this->registerRedirector();
$this->registerPsrRequest();
$this->registerPsrResponse();
$this->registerResponseFactory();
$this->registerControllerDispatcher();
}
我们先只看其中第一个 registerRouter()
方法为例.
protected function registerRouter()
{
$this->app->singleton('router', function ($app) {
return new Router($app['events'], $app);
});
}
使用 singleton 单例绑定 router
, 对应的回调函数会返回一个 Router 对象。
同时在调用回调函数的时候需要外部依赖 $app
容器作为参数。
这样就得到了路由器 router
对象用于后面操控 route
路由对象了。
总结 1.3
现在看来 register 方法就是每个 provider 的基础方法,基本包含了所有的 provider 基本逻辑程序。
1.4 调用 provider 对象的 register 方法完成以后,如果 provider 中存在两个属性
`bindings` 和 `singletons`,
就会在这个时候把这两个属性(数组集合)中的每个字段值都绑定到容器中。
其实看名字就能明白。
bindings
就是表示普通绑定。singletons
就是单例绑定。
简单说就是,provider 类中你可以配置两个数组,分别叫 bindings
和 singletons
,在 laravel 执行完 provider 的 register 方法后就可以被 laravel 依次绑定到容器中。便于后面的使用。
if (property_exists($provider, 'bindings')) {
foreach ($provider->bindings as $key => $value) {
$this->bind($key, $value);
}
}
if (property_exists($provider, 'singletons')) {
foreach ($provider->singletons as $key => $value) {
$this->singleton($key, $value);
}
}
1.5 第五步
$this->markAsRegistered($provider);
到这里要把这个已经注册完成的 provider 对象进行保存,就是存入 serviceProviders
数组中,后面再有需求的时候不需要再次进行注册。对应了第一步的作用。避免重复加载。
1.5.1 我们看下源码就知道了,做了两件事。
a. 存储这个注册的 provider 到 serviceProviders 数组中。
b. 把对应的类名存储到 loadedProviders 数组中,以备后用。
protected function markAsRegistered($provider)
{
$this->serviceProviders[] = $provider;
$this->loadedProviders[get_class($provider)] = true;
}
1.6 第六步
if ($this->isBooted()) {
$this->bootProvider($provider);
}
首先判断当前的 application 这个容器对象是否已经启动过了,什么意思呢,简单说,就是 application 的 boot 方法是否已经执行过了。
每个 provider 都提供了一个 boot 方法用来提供一些额外的逻辑在完成 provider 注册后执行。通常这些 boot 方法会在 Application 的 boot 方法中会统一执行。
代码如下:
public function boot()
{
...
array_walk($this->serviceProviders, function ($p) {
$this->bootProvider($p);
});
...
}
boot 方法的执行相对靠后,(在 application 初始化后)他是通过类 BootProviders 的 bootstrap 方法触发的。BootProviders 类又会被当做一个 serviceprovider 在 application 初始化后期和其他 serviceprovider 一起加载然后触发。先这样简单描述,不在本章讨论范围。
\Illuminate\Foundation\Bootstrap\BootProviders::class,
我们可以看到 在 laravel 中,serviceprovider 的注册一部分发生在 application 初始化后。一部分发生在 application 初始化时,当前的这个 RoutingServiceProvider 就是在初始化时。(具体参考后面 RoutingServiceProvider 源码分析)
但是有些 provider 是在 application 初始化后注册的,如果注册的时机晚于 BootProviders 的注册,这个时候 application 不会再执行它的 boot 方法了,所以需要在这一步中主动去触发 provider 的 boot。
1.6.1 provider 中是否存在 boot 方法,如果存在,触发 boot 方法中的逻辑。
protected function bootProvider(ServiceProvider $provider)
{
if (method_exists($provider, 'boot')) {
return $this->call([$provider, 'boot']);
}
}
1.7 最后返回注册好后的 provider。
整个注册完成。
总结:#
1. 注册一个 provider 之前,先去查看是不是已经注册这个服务了,避免反复注册浪费资源
2. 注册一个 provider 简单来说就是执行 provider 中的 register 方法的逻辑。
3. 注册完成后会把当前这个 provider 对象存入指定的数组中存储以便后面不再反复注册。
4.provider 通常提供 bindings
和 singletons
两个数组变量,用来提供一些 provider 需要的依赖绑定到容器中。在注册完成之后绑定。
4. 注册完成后,绑定完成后,额外的逻辑可以在 provider 的 boot 方法中实现。当然没有额外逻辑。可以不写 boot
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: