23 August 2016

依赖注入


网上已经有许多不错的文章介绍依赖注入了,维基百科-DI也有其详细的介绍。

翻译一段PHP-DI官网的解释

在没有使用依赖注入的框架代码中:

  • App需要Foo
  • App创建了Foo
  • App调用了Foo
    • Foo需要Bar
    • Foo创建Bar
    • Foo调用Bar
      • Bar需要Bim
      • Bar创建Bim
      • Bar调用Bim
        • Bim做某些事情
      • Bar做某些事情
    • Foo做某些事情

在实现了依赖注入的框架代码中:

  • App需要Foo,Foo需要Bar,Bar需要Bim
  • App创建Bim
  • App创建Bar,并提供Bim
  • App创建Foo,并提供Bar
  • App调用Foo
    • Foo调用Bar
      • Bar调用Bim
        • Bim做某些事情
      • Bar做某些事情
    • Foo做某些事情

App在这里就相当于一个IOC(Inversion of Control控制反转)容器,依赖注入是IOC最普遍的一种实现。App负责创建实例,调用方只需要告诉App需要哪个类的实例就可以了。

Laravel的容器


Laravel的App是Illuminate\Foundation\Application,该类继承了Illuminate\Container\Container(容器核心功能的实现)。

那么Laravel是怎么来使用这个App容器的呢?

首先,需要向容器声明一些依赖关系,如源码boostrap\app.php中:

<?php
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);

这段代码意思是向App容器声明,当我需要一个Illuminate\Contracts\Http\Kernel的实例时,创建一个单实例的App\Http\Kernel给我。

在源码public/index.php中:

<?php
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

这段代码的意思是向App容器请求一个Illuminate\Contracts\Http\Kernel的实例。

关于Laravel容器的使用可查看其官方文档:Service Container

Laravel的参数注入


参数注入是依赖注入的方式之一,在Laravel中,我们最经常使用到的就是参数注入,比如我们可以在某个控制器的某个方法中声明我们需要一个UserRepository类的对象作为参数传入:

<?php
    /**
     * Get user info.
     *
     * @param  UserRepository  $users
     * @return void
     */
    public function info(UserRepository $users)
    {
        $this->users = $users;
    }

这个UserRepository对象并不需要我们手动传入,框架在调用控制器的info方法的时候,会自动从容器中获取一个UserRepository的对象作为info方法的参数。

框架又是如何知道info方法需要一个UserRepository对象呢?

答案就是反射。Laravel通过PHP反射的特性:ReflectionMethod,来获取info方法依赖的参数信息。