详解如何实现Laravel的服务容器的方法示例

1. 容器的本质

  • 服务容器本身就是一个数组,键名就是服务名,值就是服务。
  • 服务可以是一个原始值,也可以是一个对象,可以说是任意数据。
  • 服务名可以是自定义名,也可以是对象的类名,也可以是接口名。
// 服务容器
$container = [
  // 原始值
  'text' => '这是一个字符串',
  // 自定义服务名
  'customName' => new StdClass(),
  // 使用类名作为服务名
  'StdClass' => new StdClass(),
  // 使用接口名作为服务名
  'Namespace\\StdClassInterface' => new StdClass(),
];

// ----------- ↓↓↓↓示例代码↓↓↓↓ ----------- //

// 绑定服务到容器
$container['standard'] = new StdClass();
// 获取服务
$standard = $container['standard'];
var_dump($standard);

2. 封装成类

为了方便维护,我们把上面的数组封装到类里面。

$instances还是上面的容器数组。我们增加两个方法,instance用来绑定服务,get用来从容器中获取服务。

class BaseContainer
{

  // 已绑定的服务
  protected $instances = [];

  // 绑定服务
  public function instance($name, $instance)
  {
    $this->instances[$name] = $instance;
  }

  // 获取服务
  public function get($name)
  {
    return isset($this->instances[$name]) ? $this->instances[$name] : null;
  }
}

// ----------- ↓↓↓↓示例代码↓↓↓↓ ----------- //

$container = new BaseContainer();
// 绑定服务
$container->instance('StdClass', new StdClass());
// 获取服务
$stdClass = $container->get('StdClass');
var_dump($stdClass);

3. 按需实例化

现在我们在绑定一个对象服务的时候,就必须要先把类实例化,如果绑定的服务没有被用到,那么类就会白白实例化,造成性能浪费。

为了解决这个问题,我们增加一个bind函数,它支持绑定一个回调函数,在回调函数中实例化类。这样一来,我们只有在使用服务时,才回调这个函数,这样就实现了按需实例化。

这时候,我们获取服务时,就不只是从数组中拿到服务并返回了,还需要判断如果是回调函数,就要执行回调函数。所以我们把get方法的名字改成make。意思就是生产一个服务,这个服务可以是已绑定的服务,也可以是已绑定的回调函数,也可以是一个类名,如果是类名,我们就直接实例化该类并返回。

然后,我们增加一个新数组$bindings,用来存储绑定的回调函数。然后我们把bind方法改一下,判断下$instance如果是一个回调函数,就放到$bindings数组,否则就用make方法实例化类。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/6002.html