Container Bind 机制

理解了前面部分的机制后,我们开始来阅读源码

首先从 Bind 绑定开始。

Illuminate\Container, Container 最重要的方法:bind

绑定分为几种:

  1. bind 把接口和其实现类绑定,当 make 解析接口的时候创建其实现类的实例对象。

  2. single 把接口和其实现类绑定,当第一次 make 解析的时候创建实例,后面都返回该实例不再创建。

  3. instance 把接口和其实现类的实例绑定,直接绑定实例对象。

  4. 上下文绑定

  5. 自动绑定

  6. tag 绑定

  7. extends 扩展绑定

先大概看 bind 下源代码:

public function bind($abstract, $concrete = null, $shared = false)
{
    $this->dropStaleInstances($abstract);
    if (is_null($concrete)) {
        $concrete = $abstract;
    }

    if (! $concrete instanceof Closure) {
        $concrete = $this->getClosure($abstract, $concrete);
    }

    $this->bindings[$abstract] = compact('concrete', 'shared');

    if ($this->resolved($abstract)) {
        $this->rebound($abstract);
    }
}
参数:

0.1 首先明确第一个参数 $abstruct 简单说就是 id,可以当做是存入容器中的名字。他可以是一个字符串,一个类,甚至是一个接口。

0.2 第二个参数 $concrete 简单说就是真实的值,可以当做是一个真正存入容器的实体。他可以是一个实现类,实例,或者一个闭包(闭包可以返回一个实现类的实例)。

0.3 第三个参数控制 Shared 的值。

方法体:
  1. 绑定前,先清空 instances 和 aliases 中存在的同名字的服务。

$this->dropStaleInstances($abstract);

1.1.dropStaleInstances($abstract) 如下,就是清空当前 instance 中和 aliases 中存在的 $abstruct 同名的服务。

protected function dropStaleInstances($abstract)
{
    unset($this->instances[$abstract], $this->aliases[$abstract]);
}
  1. 然后判断第二个参数 $concrete 是不是空,如果是空,就视 $abstruct 和 $concrete 一样。比如: app()->bind(Boss::class)。
if (is_null($concrete))
{
    $concrete = $abstract;
}
  1. 如果当前这个 $concrete 不是一个闭包。就调用 getClosure,返回一个闭包便于后面的操作。
if (! $concrete instanceof Closure)
{
    $concrete = $this->getClosure($abstract, $concrete);
}

3.1. 我们去看看这个 getClosure 方法是怎么返回一个闭包的。

很简单代码最后直接返回了一个这样形式的闭包:function($container,$parameters=[])。

一些细节:

使用 use 关键字调用父类就是 getClosure 传入的 $abstruct 和 $concrete 两个参数。

如果 $abstruct 和 $concrete 是一样的,就是如果只有一个参数,或者确实两个参数一样,像这样
app->bind(User::class,User::class),那么就调用 build 方法。

否则使用 make 方法。(build 方法和 make 方法,参看后面章节)

protected function getClosure($abstract, $concrete)
{
    return function ($container, $parameters = []) use ($abstract, $concrete) {

    if ($abstract == $concrete) {
        return $container->build($concrete);
    }

    return $container->make($concrete, $parameters);};
}

不管怎么样,代码最后直接返回了一个这样形式的闭包:function( c o n t a i n e r , container, container,parameters=[])。赋值给变量 $concrete。而这个闭包内返回的是通过 build 或者 make 解析的值

  1. 我们回到 bind 方法,上面 $concrete 得到一个闭包函数后,调用 compact 把 $concrete 和 $shard (第三个参数判断是否 shared)组成一个 key 分别为 concrete 和 shared 的数组,存入 binding 数组中,而
    binding 数组的 key 是当前的抽象类。
$this->bindings[$abstract] = compact('concrete', 'shared');

处理后结构是这样的:

$binding[$abstract] => [
    'concrete' => function($container,$parameters=[]),//getClosure()得到的
    'shared' => true/false,//shared的值是bind的第三个参数
]
  1. 接下来下一句,如果当前的抽象类曾经被解析过。那再次绑定的时候,我们要使用 rebound 函数触发 reboundCallbacks 数组中的回调函数。

关于回调函数参看前面章节

if ($this->resolved($abstract))
{
    $this->rebound($abstract);
}

如何判断当前的 $abstruct 曾经被解析过呢,我们看下 resolved 函数。两个条件

  1. 简单判断当前 resolved 数组中是否存在 $abstruct。

  2. 或者 instances 数组中是存在对应的值。但我们注意,先前在 bind 方法的第一句 $this->dropStaleInstances($abstract);的时候我们清空了 instances 对应的 $abstruct 的值,所以这边主要是考虑 $abstruct 的别名在 instances 中是否存在残留的情况。

public function resolved($abstract)
{
    if ($this->isAlias($abstract)) {
        $abstract = $this->getAlias($abstract);
    }

    return isset($this->resolved[$abstract]) ||
isset($this->instances[$abstract]);
}
总结:

在 bind 方法中。

  1. 首先移除旧的实例,如果参数 $concrete 不是闭包,是类名,会通过 getClosure 函数将类名封装进闭包中,返回这个闭包。总之 container 就要闭包。

注意:build 和 make 都是在一个闭包函数中,闭包函数不触发,它是不会创建对象的。也就是所谓的懒加载。关于 build 和 make 他是如果操作的,下几章讲解。

  1. 然后把返回的闭包函数和 share 的值组合放入 $this->bindings 数组中。

  2. 最后判断当前这个 $abstruct 是否以前被解析过,如果是,要触发对应的回调函数。

** 最简单的来说

就是原来在容器中,绑定的是一个 id 和一个闭包函数的组合。你传入闭包最好,不是闭包,laravel 会转成闭包存起来。

暂时从代码来看,我们可以猜想,最后从容器解析出来的对象是运行这个闭包产生返回的。

那我们就会有这样的猜想了,我们可以通过闭包绑定任何类型的值,因为只要在闭包中返回我们想要的任何类型的值就好了。**

实例测试

测试 1:使用闭包函数返回有依赖的对象。

class Money
{
    private $amount = 0;
    public function __construct($amount)
    {
        $this->amount = $amount;
    }

    public function getAmount()
    {
        return $this->amount;
    }
}
//注意闭包的形式

$this->app->bind('money', function($app, $parameters){return new Money($parameters[0]);});

测试 2:使用闭包函数返回没有依赖的对象

class Dollar
{
    public function getAmount(){
        return 100;
    }
}

$this->app->bind('dollar', function(){return new Dollar();});

————————————————
原文作者:HarveyNorman
转自链接:https://learnku.com/articles/41504
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请保留以上作者信息和原文链接。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值