3. IoC 与DI

一、 定义

  • 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 的总结

  1. 注解只是元数据定义,实现功能时不利用这些数据的话,没有任何作用。
  2. 使用了注解的对象,必须基于 Hyperf 和 DI容器来创建对象才能生效。
  3. 注解可以用在类、类方法、类成员属性上。
  4. DI容器是负责管理 对象的创建对象的依赖管理 的。
  5. DI容器创建出来的对象是个单例,是长生命周期对象。
  6. 通过 $container->make() 方法 或 make() 函数创建短生命周期对象。
  7. 通过 new 来实例化的对象注解 不会生效,依赖需自行管理。
相关推荐
BingoGo18 小时前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack18 小时前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack2 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo2 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack3 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理3 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
QQ5110082853 天前
python+springboot+django/flask的校园资料分享系统
spring boot·python·django·flask·node.js·php
WeiXin_DZbishe3 天前
基于django在线音乐数据采集的设计与实现-计算机毕设 附源码 22647
javascript·spring boot·mysql·django·node.js·php·html5
longxiangam4 天前
Composer 私有仓库搭建
php·composer
上海云盾-高防顾问4 天前
DNS异常怎么办?快速排查+解决指南
开发语言·php