天天看点

hyperf使用需要注意的地方

  1. jwt DateTimeImmutable 报错:

jwt DateTimeImmutable 问题 降低版本到3.3.3解决问题: composer require lcobucci/jwt:3.3.3

  1. hyperf使用注解的坑,hyperf使用注解会让注解的实例变为单例,

问题1:model产生问题 ***

如果我们注解一个model给变量,当有两个地方会查询数据,第一个查询会是A+B连接查询,第二个是A+B+C连接查询,如果第二个查询有一个参数时C的,执行二后在执行第一个会导致报错,报C中的参数不存在,单位这个实例是单例的,第二个查询的参数不会被清除,

问题二:实例化时如果带参数 ***

如果实例化带参数,第一个实例化带了一个参数,第二个操作时不会重新实例化,这样会导致第二次使用还是第一次的参数,导致出错

问题三:公共属性

在控制器中一个属性被改变,后面再有请求来获取属性时获取到是最新的,不是初始化的,

  1. Inject或Value注解不生效

    使用了构造函数中注入

    Inject

    Value

    的功能,以下两种场景,可能会导致注入失效,请注意使用。
    1. 原类没有使用

      Inject

      Value

      ,但父类使用了

      Inject

      Value

      ,且原类写了构造函数,同时又没有调用父类构造函数的情况。

    这样就会导致原类不会生成代理类,而实例化的时候又调用了自身的构造函数,故没办法执行到父类的构造函数。

    所以父类代理类中的方法

    __handlePropertyHandler

    就不会执行,那么

    Inject

    Value

    注解就不会生效。
    class ParentClass {
        /**
         * @Inject
         * @var Service
         */
        protected $value;
    }
    
    class Origin extends ParentClass
    {
        public function __construct() {}
    }
               
    1. 原类没有使用

      Inject

      Value

      ,但

      Trait

      中使用了

      Inject

      Value

    这样就会导致原类不会生成代理类,故没办法执行构造函数里的

    __handlePropertyHandler

    ,所以

    Trait

    Inject

    Value

    注解就不会生效。
    trait OriginTrait {
        /**
         * @Inject
         * @var Service
         */
        protected $value;
    }
    
    class Origin
    {
        use OriginTrait;
    }
               

基于上述两种情况,可见

原类

是否生成代理类至关重要,所以,如果使用了带有

Inject

Value

Trait

父类

时,给原类添加一个

Inject

,即可解决上述两种情况。

use Hyperf\Contract\StdoutLoggerInterface;

trait OriginTrait {
    /**
     * @Inject
     * @var Service
     */
    protected $trait;
}

class ParentClass {
    /**
     * @Inject
     * @var Service
     */
    protected $value;
}

class Origin extends ParentClass
{
    use OriginTrait;

    /**
     * @Inject
     * @var StdoutLoggerInterface
     */
    protected $logger;
}
           
  1. 一般建议使用make,而不是new,

    使用 make() 方法是为了允许 AOP 的介入,而直接 new 会导致 AOP 无法正常介入流程 2.0以后不会有这个问题,这个可以参考https://www.hyperf.wiki/2.1/#/zh-cn/changelog?id=v20-2020-06-22

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

    注意事项

    容器仅管理长生命周期的对象

    换种方式理解就是容器内管理的对象都是单例,这样的设计对于长生命周期的应用来说会更加的高效,减少了大量无意义的对象创建和销毁,这样的设计也就意味着所有需要交由 DI 容器管理的对象均不能包含

    状态

    值。

    状态

    可直接理解为会随着请求而变化的值,事实上在 协程 编程中,这些状态值也是应该存放于

    协程上下文

    中的,即

    Hyperf\Utils\Context

    短生命周期对象

    通过

    new

    关键词创建的对象毫无疑问的短生命周期的,那么如果希望创建一个短生命周期的对象但又希望使用

    构造函数依赖自动注入功能

    呢?这时我们可以通过

    make(string $name, array $parameters = [])

    函数来创建

    $name

    对应的的实例,代码示例如下:
    $userService = make(UserService::class, ['enableCache' => true]);Copy to clipboardErrorCopied
               
    注意仅

    $name

    对应的对象为短生命周期对象,该对象的所有依赖都是通过

    get()

    方法获取的,即为长生命周期的对象

    获取容器对象

    有些时候我们可能希望去实现一些更动态的需求时,会希望可以直接获取到

    容器(Container)

    对象,在绝大部分情况下,框架的入口类(比如命令类、控制器、RPC 服务提供者等)都是由

    容器(Container)

    创建并维护的,也就意味着您所写的绝大部分业务代码都是在

    容器(Container)

    的管理作用之下的,也就意味着在绝大部分情况下您都可以通过在

    构造函数(Constructor)

    声明或通过

    @Inject

    注解注入

    Psr\Container\ContainerInterface

    接口类都能够获得

    Hyperf\Di\Container

    容器对象,我们通过代码来演示一下:
    <?php
    namespace App\Controller;
    
    use Hyperf\HttpServer\Annotation\AutoController;
    use Psr\Container\ContainerInterface;
    
    class IndexController
    {
        /**
         * @var ContainerInterface
         */
        private $container;
        // 通过在构造函数的参数上声明参数类型完成自动注入
        public function __construct(ContainerInterface $container)
        {
            $this->container = $container;
        }
    }Copy to clipboardErrorCopied
               
    在某些更极端动态的情况下,或者非

    容器(Container)

    的管理作用之下时,想要获取到

    容器(Container)

    对象还可以通过

    \Hyperf\Utils\ApplicationContext::getContaienr()

    方法来获得

    容器(Container)

    对象。
  3. 中间件的执行顺序

执行顺序为:

全局中间件 -> 类级别中间件 -> 方法级别中间件

  1. 全局更改请求和响应对象

    首先,在协程上下文内是有存储最原始的 PSR-7

    请求对象

    响应对象

    的,且根据 PSR-7 对相关对象所要求的

    不可变性(immutable)

    ,也就意味着我们在调用

    $response = $response->with***()

    所调用得到的

    $response

    ,并非为改写原对象,而是一个

    Clone

    出来的新对象,也就意味着我们储存在协程上下文内的

    请求对象

    响应对象

    是不会改变的,那么当我们在中间件内的某些逻辑改变了

    请求对象

    响应对象

    ,而且我们希望对后续的 非传递性的 代码再获取改变后的

    请求对象

    响应对象

    ,那么我们便可以在改变对象后,将新的对象设置到上下文中,如代码所示:
    use Psr\Http\Message\ResponseInterface;
    use Psr\Http\Message\ServerRequestInterface;
    
    // $request 和 $response 为修改后的对象
    $request = \Hyperf\Utils\Context::set(ServerRequestInterface::class, $request);
    $response = \Hyperf\Utils\Context::set(ResponseInterface::class, $response);