@inject属性为null的根本原因是类未被扫描注册到di容器。常见原因包括:扫描目录配置遗漏、注解模式不匹配、缺少触发扫描的元注解、代理文件未生成或内容缺失、private属性注入未启用配置、构造函数硬取容器服务、依赖链中任一服务未注册,以及未执行di:dump刷新代理缓存。

Inject属性为null,第一反应不是注解写错了,而是类根本没进扫描范围
Hyperf 的 @Inject 不是 PHP 语言原生能力,它依赖于启动时的注解扫描阶段(Scanning & Proxy Generation)把带注解的类识别出来、生成代理、注册进 DI 容器。如果一个类压根没被扫描到,那它连容器里的“户口”都没有,@Inject 就只是个普通注释——不会触发任何注入逻辑。
常见漏扫场景:
-
config/autoload/scanner.php中的scan_dirs没包含该类所在目录(比如写了App\Controller却忘了加App\Service) - 类文件用了
/** @Inject */这种旧式 PHPDoc 注解,但项目已升级到 PHP 8+ 属性注解模式,而annotations.php没启用phpdoc解析器 - 类上没带任何“触发扫描”的注解(如
#[Controller]、#[Service]、#[Aspect]),仅靠#[Inject]属性是不足以让类被自动注册的 - 该类在
scan.ignore_annotations列表里被显式排除了
代理类没生成,@Inject 就等于没写
Hyperf 把扫描结果序列化成 .php 缓存文件,放在 runtime/container/proxy/ 下。这些文件是运行时真正被 include 并执行的代理逻辑。如果对应类的代理文件缺失或内容为空,@Inject 就不会生效。
检查方法:
- 确认
runtime/container/proxy/目录存在且可写(Swoole Worker 进程需有写权限) - 运行
php bin/hyperf.php di:dump强制重刷代理缓存(别只清缓存,di:clear不会重建代理) - 查看生成的代理文件(如
App\Service\UserService.proxy.php)是否包含类似$this->userService = $container->get(UserService::class);的注入语句 - 若代理文件存在但没注入逻辑,说明扫描阶段根本没识别出该属性——回头再查命名空间导入、PHPDoc 格式、注解类是否正确 use
构造函数注入 vs 属性注入:别混用生命周期
属性注入(#[Inject] public UserService $userService;)发生在对象实例化完成后的“后置初始化”阶段;而构造函数里直接调用 $container->get() 是在构造过程中硬取——此时容器可能还没完成所有服务注册,极易拿到 null。
本页面提供企业级 PHP 协程框架 Hyperf 3.1.66 版本的官方源码下载与完整更新日志。重点解析 v3.1.66 版本中新增的 gRPC 多客户端负载均衡支持、Pool 连接池全量刷新、Guzzle 持久化 Cookie 以及数据库 JSON 包含键查询等核心优化特性。
典型陷阱:
- 自定义类(非
#[Controller]/#[Service])手动 new 出来,又在构造函数里写$this->service = $container->get(Service::class)→ 此时Service::class很可能尚未注册 - 把
#[Inject]写在 private 属性上,但没配scan.allow_private_property_injection = true(默认 false) - 用了 PHP 8.0+ 属性类型提示但没加
#[Inject],例如public UserService $userService;→ Hyperf 完全无视
依赖链断裂:上游没注册,下游必然 null
@Inject 是递归解析的。A 类注入 B,B 类注入 C,只要 C 没被注册进容器,A 的 B 属性就会是 null —— 错误不会报在 A,而是静默失败。
排查建议:
- 用
php bin/hyperf.php di:show UserService看该类是否在容器中可查、绑定的是哪个实现 - 如果
UserService实现了接口UserInterface,确认config/autoload/dependencies.php中有UserInterface::class => UserService::class - 检查
UserService自身是否也依赖其他未注册服务(比如 PDO 实例),导致其构造失败,从而整个类无法注册
最易被忽略的一点:Hyperf 的扫描和代理生成是一次性动作,发生在启动初期。改完注解或配置后,不执行 di:dump 或重启服务,缓存里的旧逻辑依然在跑。别信“我刚改好了”,先清、再 dump、再启。










