二:依赖注入

一:Hyperf 介绍

 

 Hyperf 设计理念

Hyperf常用命令行

(1):查看命令列表

  php bin/hyperf.php list

(2):启动命令

 php bin/hyperf.php start

(2):查看路由注册状态

 php bin/hyperf.php describe:routes

(3):创建控制器

 php bin/hyperf.php gen:controller IndexController

(4):创建模型

 php bin/hyperf.php gen:model User

 

 

二:Hyperf的路由  https://hyperf.wiki/2.2/#/zh-cn/router

 

 

 

 

 

 

 

一:简单对象注入

  (1):通过构造方法注入

  假设我们需要在 GoodsController 内调用 GoodsService 类的 getInfoById(string $id) 方法。

  

  

 

 

    

 

 

   注意使用构造函数注入时,调用方也就是 IndexController 必须是由 DI 创建的对象才能完成自动注入(依赖注入的依赖关系都是容器作为第三方调度器,使其各个对象关联起来),而 Controller 默认是由 DI 创建的,所以可以直接使用构造函数注入

  当您希望定义一个可选的依赖项时,可以通过给参数定义为 nullable 或将参数的默认值定义为 null,即表示该参数如果在 DI 容器中没有找到或无法创建对应的对象时,不抛出异常而是直接使用 null 来注入。*(该功能仅在 1.1.0 或更高版本可用)*

  

 

 

   

 

 

 

    (2):通过@Inject注解注入

      假设我们需要在 ItemController 内调用 ItemService 类的 getInfoById(string $id) 方法。

    

 

 

     

    

    通过 @Inject 注解注入可作用于 DI 创建的(单例)对象,也可作用于通过 new 关键词创建的对象;

    使用 @Inject 注解时需 use Hyperf\Di\Annotation\Inject; 命名空间;

 

    

Required 参数

@Inject 注解存在一个 required 参数,默认值为 true,当将该参数定义为 false 时,则表明该成员属性为一个可选依赖,当对应 @var 的对象不存在于 DI 容器或不可创建时,将不会抛出异常而是注入一个 null,如下:

    

 

     

     

二:抽象对象注入

   基于上面的例子,从合理的角度上来说,Controller 面向的不应该直接是一个 UserService服务 类,可能更多的是一个 UserServiceInterface 的接口类,此时我们可以通过 config/autoload/dependencies.php 来绑定对象关系达到目的,我们还是通过代码来解释一下。

定义一个接口类:

  

 

   在 config/autoload/dependencies.php 内完成关系配置:

  

 

   

 

   

 

 三:工厂对象注入

  我们假设 MessageService 的实现会更加复杂一些,在创建 MessageService 对象时构造函数还需要传递进来一些非直接注入型的参数,假设我们需要从配置中取得一个值,然后 MessageService 需要根据这个值来决定是否开启缓存模式(顺带一说 Hyperf 提供了更好用的 模型缓存 功能)

  通过构造函数注入的参数,应该是一个应用参数,而不是一个动态的跟请求相关的参数,因为所有从DI容器里创建出来的对象都是一个单例,在Hyperf的生命周期里这个单例是进程全局的单例对象,也就是说这个对象有可能被多个请求同时使用的,也就是说我们所有从DI取出来的对象的构造函数,或者成员属性都不应该是与请求相关的动态参数,只能是一个固定的配置,或者从配置文件读取的配置,或者是其它对象的一个依赖;

    我们需要创建一个工厂来生成 MessageService 对象:

    

 

    1:通过工厂类来创建复杂的对象;

    2:定义一个工厂类于__invoke()方法内实现对象的创建并返回;

    3:make()函数创建短生命周期对象;

     其中提供了__invoke()这个方法;这个方法里提供了container这个参数;这个container默认的也就是注入Hyperf的DI容器对象;通过这个对象是可以从Hyperf的DI容器里面取出其它任意的对象;

    首先从容器取出配置的一个服务类ContainerInterface,该服务类对应于Hyperf\Contract\ConfigInterface;通过get方法取出对应的配置;

    make(MessageService::class, compact('enableCache')); 相当于new一个MessageService这个类并且在MessageService的构造函数中传递参数'enableCache';

     compact('enableCache')解释:取当前方法内跟这个字符串同名的变量的值形成key-value这个关系key就是enableCache而value就是enableCache对应的变量的值,这里也就是上面$enableCache的值;

    最终返回一个实例化好的MessageService

    调整接口类与工厂类的关系;注入的即为由工厂类创建的对象;

    

 

     这样在注入 UserServiceInterface 的时候容器就会交由 UserServiceFactory 来创建对象了。

     

 

     

 

     

    在 config/autoload/dependencies.php 调整绑定关系:

  

 

 

  这样在注入 MessageServiceInterface 的时候容器就会交由 MessageServiceFactory 来创建对象了。

  当然在该场景中可以通过 @Value 注解来更便捷的注入配置而无需构建工厂类,此仅为举例

短生命周期对象

  通过 new 关键词创建的对象毫无疑问的短生命周期的,那么如果希望创建一个短生命周期的对象但又希望使用 构造函数依赖自动注入功能 呢?这时我们可以  通过 make(string $name, array $parameters = []) 函数来创建 $name 对应的的实例,代码示例如下:

  $userService = make(UserService::class, ['enableCache' => true]);

注意仅 $name 对应的对象为短生命周期对象,该对象的所有依赖都是通过 get() 方法获取的,即为长生命周期的对象,可理解为该对象是一个浅拷贝的对象

获取容器对象

  (1):有些时候我们可能希望去实现一些更动态的需求时,会希望可以直接获取到 容器(Container) 对象,在绝大部分情况下,框架的入口类(比如命令类、控制器、RPC 服务提供者等)都是由 容器(Container) 创建并维护的,也就意味着您所写的绝大部分业务代码都是在 容器(Container) 的管理作用之下的,也就意味着在绝大部分情况下您都可以通过在 构造函数(Constructor) 声明或通过 @Inject 注解注入 Psr\Container\ContainerInterface 接口类都能够获得 Hyperf\Di\Container 容器对象,我们通过代码来演示一下:

  

 

 

   (2):在某些更极端动态的情况下,或者非 容器(Container) 的管理作用之下时,想要获取到 容器(Container) 对象还可以通过 \Hyperf\Utils\ApplicationContext::getContaienr() 方法来获得 容器(Container) 对象。

  $container = \Hyperf\Utils\ApplicationContext::getContainer();

    

  

 

   

 

 

 

  

posted @ 2022-02-17 11:54  痞子胥  阅读(239)  评论(0)    收藏  举报