当前位置: 首页 > article >正文

3. IoC 与DI

一、 定义

  • IoC,即控制反转,把对象的调用权交给容器,通过容器来实现对象的装配和管理。
  • DI,即依赖注入,对象之间依赖关系由容器在运行期决定,由容器动态的将依赖关系注入到对象之中。
  • DI,是对IoC更完善的描述。

二、 疑问

  • 谁依赖谁?【对象实例化依赖容器】
  • 为什么要依赖?【对象实例化通过容器自动得到外部依赖】
  • 谁注入谁?【容器注入对象的依赖到对象中】
  • 注入了什么?【注入了对象的外部依赖】

三、 Hyperf的依赖注入的实现

  • 由hyperf/di 组件提供功能支持
  • 更符合长生命周期的应用使用
  • 提供了 注解、注解注入、AOP
  • 基于 PSR-11 实现,可独立应用于其它框架

四、注入方式

  • 通过构造方法注入
  • 通过#[Inject]注解注入

五、 注入类型

  • 简单对象注入
  • 抽象对象注入
  • 工厂对象注入

六、 实例

① 【简单对象注入】

  • 假设存在一个UserService类,在IndexController类中引用它
<?php
// UserService类
namespace App\Service;
class UserService {
  public fucntion getInfoById(int $id) {
    // 假设存在一个 Info  实体
    return (new Info())-> fill($id);
  } 
}

1. 构造函数的形式

构造函数定义依赖类的 Typehint
IndexController 在被DI容器创建时,会自动注入相关依赖

<?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
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
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
// 在 config/dependencies.php 内
use App\Service\UserServiceInterface;
use App\Service\UserService;
return [
  'dependencies' => [
    UserServiceInterface::class =>UserService::class,
  ],
];

3. 通过以接口类作为 Typehint 注入对应的实现类

<?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
    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
    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
    // 在 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 来实例化的对象注解 不会生效,依赖需自行管理。

http://www.kler.cn/news/366515.html

相关文章:

  • 【Java】java 集合框架(详解)
  • VUE中文本域默认展示最底部内容
  • 【Flutter】基础组件:Container
  • 如何使用gitlab切换分支
  • Rust的move关键字在线程中的使用
  • 艾默生Ovation伺服驱动器电子模块(1C31199G03)
  • python如何读取Excel文件!
  • 使用virtualenv/Anaconda/Miniconda创建python虚拟环境
  • 实战分享:Golang中实现高性能日志记录与错误跟踪的艺术
  • Linux:sort与uniq
  • JavaScript 第23章:WebSocket 与实时通讯
  • sql获取时间差
  • ssh连接报错
  • 【Java设计模式】1-15章
  • 【Linux 从基础到进阶】性能测试工具使用(sysbench、fio等)
  • Linux 部署 mysql
  • vue3可组合函数和hook的用法和使用场景区别
  • 使用React构建现代Web应用
  • <Project-11 Calculator> 计算器 0.3 年龄计算器 age Calculator HTML JS
  • 服务攻防之Redis数据库安全
  • 2024.10.25 软考学习笔记(知识点)
  • 通过Conda安装jupyter notebook
  • 【HTML】之form表单元素详解
  • 远程服务器训练网络本地读取TensorBoard
  • 【设备状态与人员动态的监测和呈现-会议签到的补充】
  • Android 开发 调节声音 SeekBar自定义样式