Container share机制与alias别名

思考并回答以下问题:

  • app()->alias(‘money’,’alias_money’);是这样使用吗?

Share机制

简介

Laravel的Container中有个shared机制。

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Determine if a given type is shared.
*
* @param string $abstract
* @return bool
*/
public function isShared($abstract)
{
return isset($this->instances[$abstract]) ||
(isset($this->bindings[$abstract]['shared']) &&
$this->bindings[$abstract]['shared'] === true);
}

满足shared只要满足两个条件中的任意一个:

  • 1.instances数组中存在,说明这个数组中的所有数据都是shard。我们通过名字就知道存储的是实例化的对象。
  • 2.或者,还未实例化,但是已经绑定在数组bindings中并且参数shard==true的类。

总结

通过shared我们知道,share就是一个标记。

  • 1.instances这个实例化的数组中都是shared的。
  • 2.未实例化但是已经绑定的数组binging中要注明sharde是true他才是sharde的。

sharde我猜想首先可以用作单例,和其他类共享单个实例,直接从instance中获取便可以了,总的来说就是一个标记。

Alias别名

简介

服务别名也相对重要,在很多地方使用。

通过服务绑定的别名,在解析服务的时候,跟不使用别名的效果一致。别名的作用也是为了同时支持全类型的服务绑定名称以及简短的服务绑定名称考虑的。

通俗的讲,假如我们想要创建auth服务,我们既可以这样写:

1
$this->app->make('auth')

又可以写成:

1
$this->app->make('\Illuminate\Auth\AuthManager::class')

还可以写成

1
$this->app->make('\Illuminate\Contracts\Auth\Factory::class')

后面两个服务的名字都是auth的别名,使用别名和使用auth的效果是相同的。

这里特别注意,谁是谁的别名。

已测示例

我们用别名alias_money重新命名了Money::class,别名先找到‘money’再去找到Money::class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Money
{
public function getAmount()
{
return 100;
}
}

class ExampleTest extends TestCase
{
public function testClosure()
{
app()->bind('money', Money::class);
app()->alias('money','alias_money');

$boss= app()->make('alias_money');

$output = $boss->getAmount();
$this->assertEquals($output, 100);
}
}

服务别名的获取

我们看下源码:

  • 1.看看有没有对应的$abstract的别名。如果没有就返回当前$abstract。
  • 2.如果发现别名和$abstract一样,抛出异常,别名不能和自己一样。
  • 3.进一步递归,查看这个别名是否还有别名的别名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Get the alias for an abstract if available.
*
* @param string $abstract
* @return string
*/
public function getAlias($abstract)
{
if (! isset($this->aliases[$abstract])) {
return $abstract;
}

return $this->getAlias($this->aliases[$abstract]);
}

服务别名的实现

那么这些别名是如何加载到服务容器里面的呢?

两种方法:

1、通过调用Container中的alias方法,我们看下源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Alias a type to a different name.
*
* @param string $abstract
* @param string $alias
* @return void
*
* @throws \LogicException
*/
public function alias($abstract, $alias)
{
if ($alias === $abstract) {
throw new LogicException("[{$abstract}] is aliased to itself.");
}

$this->aliases[$alias] = $abstract;

$this->abstractAliases[$abstract][] = $alias;
}

这里有两个数组:aliases和abstractAliases,他们保存一样的数据,但是键值对相反。简单说就是aliases的key值是abstractAliases的value值。把key和value分别存储到对应的数组中。

2、外部配置引入。

其实原理也是一样,使用alias()方法,但是laravel在初始化的时候自己读取配置文件去存入对应数组中。

有两个地方,一个是app.php文件下的alias数组中,还有一个是在laravel/framwork/src/Illuminate/Foundation/Application.php下的registerCoreContainerAliases方法,这个方法在Application初始化的时候会加载。Application是Container的子类,应用容器。后面再讨论。

看部分代码实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$aliases = [
'app' => [
\Illuminate\Foundation\Application::class,
\Illuminate\Contracts\Container\Container::class,
\Illuminate\Contracts\Foundation\Application::class
],
'auth' => [
\Illuminate\Auth\AuthManager::class,
\Illuminate\Contracts\Auth\Factory::class
],
'auth.driver' => [
\Illuminate\Contracts\Auth\Guard::class
],
'blade.compiler' => [
\Illuminate\View\Compilers\BladeCompiler::class
],
'cache' => [
\Illuminate\Cache\CacheManager::class,
\Illuminate\Contracts\Cache\Factory::class
],
...
]

运行的registerCoreContainerAliases方法。我们可以看到还是调用了alias()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Register the core class aliases in the container.
*
* @return void
*/
public function registerCoreContainerAliases()
{
foreach ([$aliases] as $key => $aliases) {
foreach ($aliases as $alias) {
$this->alias($key, $alias);
}
}
}

加载后,服务容器的aliases和abstractAliases数组实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$aliases = [
'Illuminate\Foundation\Application' = "app"
'Illuminate\Contracts\Container\Container' = "app"
'Illuminate\Contracts\Foundation\Application' = "app"
'Illuminate\Auth\AuthManager' = "auth"
'Illuminate\Contracts\Auth\Factory' = "auth"
'Illuminate\Contracts\Auth\Guard' = "auth.driver"
'Illuminate\View\Compilers\BladeCompiler' = "blade.compiler"
'Illuminate\Cache\CacheManager' = "cache"
'Illuminate\Contracts\Cache\Factory' = "cache"
...


$abstractAliases = [
app = {array} [3]
0 = "Illuminate\Foundation\Application"
1 = "Illuminate\Contracts\Container\Container"
2 = "Illuminate\Contracts\Foundation\Application"
auth = {array} [2]
0 = "Illuminate\Auth\AuthManager"
1 = "Illuminate\Contracts\Auth\Factory"
auth.driver = {array} [1]
0 = "Illuminate\Contracts\Auth\Guard"
blade.compiler = {array} [1]
0 = "Illuminate\View\Compilers\BladeCompiler"
cache = {array} [2]
0 = "Illuminate\Cache\CacheManager"
1 = "Illuminate\Contracts\Cache\Factory"
...
]

我们在这里还是要特别强调:

数组$abstractAliases中,app是abstract,后面跟着的三个数组元素才是‘app’的别名(alias)
1
2
3
4
app = {array} [3]
0 = "Illuminate\Foundation\Application"
1 = "Illuminate\Contracts\Container\Container"
2 = "Illuminate\Contracts\Foundation\Application"

这里的这个结构需要指出的是,不管这个app别名有多少个,这些别名都指向一个abstract,这个abstract又会指向数组binding,binding数组是建立abstract和concrete关系的数组,而这个$abstractAliases和$aliases则是建立abstruc和alias之间关系的数组。

总结

别名机制就是用一个别名来命名具体对象,类或者闭包函数。他在Facade中有运用到,以及在容器绑定和解析都有用到。我们只要记住,他存储的两个数组名字:

  • 1.abstractAliases
  • 2.abstracts

我觉得他主要的作用可能就是让代码简洁吧。

0%