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

上面的代码用singleton绑定了一个名为anonymous的服务,回调函数里返回了一个匿名类的实例。这个匿名类在被实例化时会输出一段文字。无论我们make多少次anonymous,这个回调函数只会被执行一次,匿名类也只会被实例化一次。

5. 自动注入

自动注入是Ioc容器的核心,没有自动注入就无法做到控制反转。

自动注入就是指,在实例化一个类时,用反射类来获取__construct所需要的参数,然后根据参数的类型,从容器中找到已绑定的服务。我们只要有了__construct方法所需的所有参数,就能自动实例化该类,实现自动注入。

现在,我们增加一个build方法,它只接收一个参数,就是类名。build方法会用反射类来获取__construct方法所需要的参数,然后返回实例化结果。

另外一点就是,我们之前在调用make方法时,如果传的是一个未绑定的类,我们直接new了这个类。现在我们把未绑定的类交给build方法来构建,因为它支持自动注入。

class InjectionContainer extends SingletonContainer
{

  // 获取服务
  public function make($name)
  {
    if (isset($this->instances[$name])) {
      return $this->instances[$name];
    }
    if (isset($this->bindings[$name])) {
      // 执行回调函数并返回
      $instance = call_user_func($this->bindings[$name]['callback']);

      if ($this->bindings[$name]['shared']) {
        // 标记为单例时,存储到服务中
        $this->instances[$name] = $instance;
      }
    } else {
      // 使用build方法构建此类
      $instance = $this->build($name);
    }

    return $instance;
  }

  // 构建一个类,并自动注入服务
  public function build($class)
  {

    $reflector = new ReflectionClass($class);

    $constructor = $reflector->getConstructor();

    if (is_null($constructor)) {
      // 没有构造函数,直接new
      return new $class();
    }

    $dependencies = [];

    // 获取构造函数所需的参数
    foreach ($constructor->getParameters() as $dependency) {
      if (is_null($dependency->getClass())) {
        // 参数类型不是类时,无法从容器中获取依赖
        if ($dependency->isDefaultValueAvailable()) {
          // 查找参数的默认值,如果有就使用默认值
          $dependencies[] = $dependency->getDefaultValue();
        } else {
          // 无法提供类所依赖的参数
          throw new Exception('找不到依赖参数:' . $dependency->getName());
        }
      } else {
        // 参数类型是类时,就用make方法构建该类
        $dependencies[] = $this->make($dependency->getClass()->name);
      }
    }

    return $reflector->newInstanceArgs($dependencies);
  }
}

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

class Redis
{
}

class Cache
{
  protected $redis;

  // 构造函数中依赖Redis服务
  public function __construct(Redis $redis)
  {
    $this->redis = $redis;
  }
}

$container = new InjectionContainer();
// 绑定Redis服务
$container->singleton(Redis::class, function () {
  return new Redis();
});
// 构建Cache类
$cache = $container->make(Cache::class);
var_dump($cache);


      

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

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