一、 定义
- IoC,即控制反转,把对象的调用权交给容器,通过容器来实现对象的装配和管理。
- DI,即依赖注入,对象之间依赖关系由容器在运行期决定,由容器动态的将依赖关系注入到对象之中。
- DI,是对IoC更完善的描述。
二、 疑问
- 谁依赖谁?【对象实例化依赖容器】
- 为什么要依赖?【对象实例化通过容器自动得到外部依赖】
- 谁注入谁?【容器注入对象的依赖到对象中】
- 注入了什么?【注入了对象的外部依赖】
三、 Hyperf的依赖注入的实现
- 由hyperf/di 组件提供功能支持
- 更符合长生命周期的应用使用
- 提供了 注解、注解注入、AOP
- 基于 PSR-11 实现,可独立应用于其它框架
四、注入方式
- 通过构造方法注入
- 通过#[Inject]注解注入
五、 注入类型
- 简单对象注入
- 抽象对象注入
- 工厂对象注入
六、 实例
① 【简单对象注入】
- 假设存在一个UserService类,在IndexController类中引用它
php
<?php
// UserService类
namespace App\Service;
class UserService {
public fucntion getInfoById(int $id) {
// 假设存在一个 Info 实体
return (new Info())-> fill($id);
}
}
1. 构造函数的形式
构造函数定义依赖类的 Typehint
IndexController 在被DI容器创建时,会自动注入相关依赖
php
<?php
// IndexController
namespace App\Controller;
class IndexController {
private $userService;
public function __construct(UserService $userService) {
$this->userService = $userService;
}
public function index() {
return $this->userService->getInfoById(1);
}
}
2. Inject注解的形式
在类成员属性上定义 #[Inject] 注解 和 @var,完成依赖注入
php
<?php
namespace App\Controller;
use App\Service\UserSerice;
use Hyperf\Di\Annotation\Inject;
class IndexController {
#[Inject]
private UserService $userService;
public function index() {
return $this->userService->getInfoById(1);
}
}
②【抽象对象注入】
1. 定义一个接口类 UserServiceInterface
UserService 实现接口类
php
<?php
namespace App\Service;
// UserServiceInterface 接口类
interface UserServiceInterface {
public function getInfoById(int $id);
}
// UserService 实现类
class UserService implements UserServiceInterface {
public function getInfoById(int $id) {
return (new Info())->fill($id);
}
}
2. 在对应的位置,进行 接口类 与 实现类 的关系绑定
php
<?php
// 在 config/dependencies.php 内
use App\Service\UserServiceInterface;
use App\Service\UserService;
return [
'dependencies' => [
UserServiceInterface::class =>UserService::class,
],
];
3. 通过以接口类作为 Typehint 注入对应的实现类
php
<?php
namespace App\Controller;
use App\Service\UserServiceInterface;
use Hyperf\Di\Annotation\Inject;
class IndexController {
/**
* @var UserServiceInterface
*/
#[Inject]
private $userService;
public function index() {
return $this-<userService->getInfoById(1);
}
}
③【工厂对象注入】
-
通过容器来创建一个复杂类,如构造函数需要接收参数的,参数应是应用参数,而不是动态的请求参数。
-
DI管理的对象是单例
php<?php namespace App\Service; class UserService inplements userServiceInterface { private $enableCache; public function __construct(bool $enableCache) { $this->enableCache = $enableCache; } public function getInfoById(int $id) { return (new Info())->fill($id); } }
-
通过工厂类创建复杂的对象
- 定义一个工厂类,在__invoke()方法内实现对象的创建并返回
- make() 函数创建短声明周期对象
php<?php namespace App\Service; use Hyperf\Contract\ConfigInterface; use Psr\Container\ContainerInterface; class UserServiceFactory { // __invoke() 方法写成对象的生产 // 方法参数会自动注入一个当前的容器实例 // 通过$container对象可以取出hyperf\Di容器中的任意对象 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')); } }
-
调整接口类与工厂类的关系,注入的即为 由工厂类创建的对象
php<?php // 在 config/dependencies.php 内 use App\Service\UserServiceInterface; use App\Service\UserServiceFactory; return [ 'dependencies' => [ UserServiceInterface::class =>UserServiceFactory::class, ], ];
④ 注入容器自身
- 直接注入 Psr\Container\ContainerInterface
- 通过 Hyperf\Utils\ApplicationContext::getContainer() 获得
七、 注解 和 DI 的总结
- 注解只是元数据定义,实现功能时不利用这些数据的话,没有任何作用。
- 使用了注解的对象,必须基于 Hyperf 和 DI容器来创建对象才能生效。
- 注解可以用在类、类方法、类成员属性上。
- DI容器是负责管理 对象的创建 和 对象的依赖管理 的。
- DI容器创建出来的对象是个单例,是长生命周期对象。
- 通过 $container->make() 方法 或 make() 函数创建短生命周期对象。
- 通过 new 来实例化的对象注解 不会生效,依赖需自行管理。