依赖注入和控制反转

  • 之前在laravel会用到依赖注入和控制反转,面试也会遇到,一直觉得自己明白的很透彻了,现在hyperf框架又遇到,发现疑问重重,特此整理一下,以免后期再不明白

概念

1. 依赖注入(DI)

  • 对象之间依赖关系由容器在运行期决定,由容器动态的将依赖关系注入到对象之中

2. 控制反转(Ioc)

  • 即把对象的调用权反转交给容器进行装配和管理

注入的实现

1. 普通注入
1.1 简单对象注入
Controller
nullablenullnull
namespace App\Controller;
use App\Service\UserService;class IndexController
{/*** @var UserService*/private $userService;// 通过在构造函数的参数上声明参数类型完成自动注入public function __construct(?UserService $userService){$this->userService = $userService;}public function index(){$id = 1;if ($this->userService instanceof UserService) {// 仅值存在时 $userService 可用return $this->userService->getInfoById($id);    }return null;}
}
1.2 通过 @Inject 注解注入
IndexControllernew@Injectuse Hyperf\Di\Annotation\Inject;@Injectrequiredtruefalse@varnull
namespace App\Controller;
use App\Service\UserService;
use Hyperf\Di\Annotation\Inject;class IndexController
{/*** 通过 `@Inject` 注解注入由 `@var` 注解声明的属性类型对象* @Inject* @var UserService*/private $userService;public function index(){$id = 1;// 直接使用return $this->userService->getInfoById($id);}
}
  • 注意:Inject注解实现原理:当注解定义在类属性时被扫描时会触Inject注解类下面的collectProperty方法,然后会取到该对象,实现注入(这里hyperf有一个static::$container静态变量,在启动时只会被修改,不随协成改变,所有的依赖关系都会被映射的存储在改静态变量里面。需要是直接取改静态变量里的);
  • 注:直接调用Inject的类时不会被重复实例化,注意避坑;
2. 依赖注入
2.1 抽象对象注入
UserServiceUserServiceInterfaceconfig/autoload/dependencies.php
2.1.1定义接口类
namespace  App\\Service;interface UserServiceInterface
{public function getInfoById(int $id);
}
2.1.2 UserService 实现接口类
namespace App\Service;class UserService implements UserServiceInterface
{public function getInfoById(int $id){// 我们假设存在一个 Info 实体return (new Info())->fill($id);}
}
2.1.3 在config/autoload/dependencies.php内完成关系配置
return [\App\Service\UserServiceInterface::class => \App\Service\UserService::class
];
2.1.4 Contoller注入接口,会自动根据配置进行注入
namespace App\Controller;
use App\Service\UserServiceInterface;
use Hyperf\Di\Annotation\Inject;class IndexController
{/*** @Inject* @var UserServiceInterface*/private $userService;public function index(){$id = 1;// 直接使用return $this->userService->getInfoById($id);}
}
2.2 工厂对象注入
UserServiceInterfaceUserServiceFactoryUserServiceUserServiceUserService
2.2.1 创建工厂类生成 UserService 对象
namespace App\Service;
use Hyperf\Contract\ConfigInterface;
use Psr\Container\ContainerInterface;class UserServiceFactory
{// 实现一个 __invoke() 方法来完成对象的生产,方法参数会自动注入一个当前的容器实例public function __invoke(ContainerInterface $container){$config = $container->get(ConfigInterface::class);// 我们假设对应的配置的 key 为 cache.enable$enableCache = $config->get('cache.enable', false);// make(string $name, array $parameters = []) 方法等同于 new ,使用 make() 方法是为了允许 AOP 的介入,而直接 new 会导致 AOP 无法正常介入流程return make(UserService::class, compact('enableCache'));}
}
2.2.2 创建UserService类,其中构造函数提供了一个参数
namespace App\Service;class UserService implements UserServiceInterface
{/*** @var bool*/private $enableCache;public function __construct(bool $enableCache){// 接收值并储存于类属性中$this->enableCache = $enableCache;}public function getInfoById(int $id){return (new Info())->fill($id);}
}
2.2.3 在config/autoload/dependencies.php调整绑定关系
return [\App\Service\UserServiceInterface::class => \App\Service\UserServiceFactory::class
];
容器@Inject