hyperf 3.1 属性注入必须显式使用 #[inject],仅支持 public/protected 非只读属性,类需有@service等注解且扫描路径正确,改注解后须清缓存并确保自动加载。

属性注入必须显式加 #[Inject]
Hyperf 3.1 不识别 PHP 8 的原生属性类型提示(如 public UserService $userService),也不认注释写法(如 // @Inject)。只认 #[Inject] 这个 attribute。漏写、错写成注释、或写在 setter 方法上,都会导致注入失败。
-
#[Inject]必须直接写在属性声明行上方或同一行 - 属性不能是
private——protected或public才支持(private readonly也不行) - 类本身必须被容器扫描到:至少得有
#[Service]、#[Controller]等有效注解,否则整个类都不会注册进容器
缓存不干净会导致 #[Inject] 看似失效
常见现象是改了代码、加了 #[Inject],但运行时属性还是 null。根源往往是旧注解缓存没清掉,尤其是 runtime/container/annotation 目录下的扫描结果没更新。
- 改完注解后,必须执行
php bin/hyperf.php cache:clear - 如果用
server:watch,要先停掉它,再清缓存,再重启;否则热更新可能跳过新注解解析 - 确认
config/autoload/annotations.php中的scan.paths包含你放类的目录(比如/Domain或/Infrastructure),否则类根本不会被扫描 - 运行
composer dump-autoload -o确保类能被自动加载,不然注解连解析机会都没有
构造函数注入和属性注入的执行顺序
一个类同时用了构造函数注入和属性注入时,Hyperf 会先完成构造函数实例化,再执行属性注入。这意味着:
- 属性注入的值在
__construct()里还不可用,别在构造函数里访问$this->userService - 如果属性注入依赖某个构造函数参数(比如需要
$this->config来决定注入哪个实现),这种跨阶段依赖无法自动满足,得手动处理 - 属性注入默认是单例行为,且不支持
#[Lazy]加在构造函数参数上——它只对#[Inject]属性生效
父类有 @Inject 但子类重写了 __construct 就会失效
这是个隐蔽但高频的问题:父类用了 #[Inject] 属性,子类继承后自己写了 __construct() 却没调用 parent::__construct(),结果父类的属性注入完全不触发。
- 原因:Hyperf 的代理类机制依赖父类构造函数执行来触发属性注入逻辑,子类覆盖后中断了这个流程
- 解决方式只有两个:要么子类构造函数里显式调用
parent::__construct(),要么把注入逻辑移到子类自己身上(加#[Inject]) - trait 中的
#[Inject]同样受此限制——如果类用了 trait 且自己定义了构造函数,又没手动触发 trait 注入逻辑,那注入也会静默丢失
scan.paths 漏了一个目录,或者 runtime/container/annotation 里还躺着上周的缓存,#[Inject] 就永远不生效。











