必须确保容器接管类、接口正确绑定、属性可写,否则@inject失效:controller需由容器创建,接口须在dependencies.php中绑定实现,@inject属性必须private且带@var注解,切面类也要纳入容器管理。

直接改用 @Inject 注解或构造函数注入,不等于代码就自动解耦、可测试、能跑起来——多数人在迁移时卡在容器没接管类、接口没绑定、属性不可写这三步上。
Controller 必须由容器创建,否则注入全失效
传统 MVC 里手动 new IndexController() 是常见操作,但在 Hyperf 中这会绕过 DI 容器,导致构造函数参数不解析、@Inject 属性不赋值,运行时报 Typed property must not be accessed before initialization 或 Cannot resolve UserService。
- 确保控制器通过路由访问(如 HTTP 请求触发),或显式用
ApplicationContext::get(IndexController::class)获取实例 - 检查类是否在注解扫描路径内:确认
config/autoload/annotations.php的'scan' => true已启用,且'paths'包含app/Controller - 类必须有完整命名空间和 PSR-4 合规路径,例如
App\Controller\IndexController对应app/Controller/IndexController.php
接口注入必须配 dependencies.php,不能只写类型提示
把 private UserServiceInterface $userService 往构造函数或 @Inject 属性里一放,不配 dependencies.php,容器根本不知道该注入哪个实现,直接抛 ContainerException。
- 配置必须用类常量,不是字符串:
UserServiceInterface::class => UserService::class✅;'App\Service\UserServiceInterface' => 'App\Service\UserService'❌ - 右边必须是具体可实例化的类,不能是抽象类或接口
- 如果接口有多个实现(比如
CacheUserService和DbUserService),需明确绑定一个,否则容器无法抉择
@Inject 只支持 private 属性 + @var 类型声明
@Inject 看似简单,但漏掉任意一项就会静默失败:属性为 null,不报错,只在调用时崩。
- 属性必须是
private,public被跳过,protected在部分版本中不稳定,统一用private - 必须紧挨着写
@var注解,且类型要精确:/** @var UserService */ #[Inject] private UserService $userService;✅;#[Inject] private $userService;❌ - PHP 8.2+ 的
readonly属性不能被@Inject修改,只能走构造函数注入
AOP 切面类也要进容器,否则@Inject 是空的
很多人把切面类当普通工具类写,new LogAspect() 后发现 @Inject 属性全是 null——因为切面本身没被容器管理,注解根本没触发。
- 切面类必须被容器识别:要么加
#[Aspect]并确保所在目录在注解扫描路径中,要么在dependencies.php显式绑定 - 切面方法
process()的参数只支持ProceedingJoinPoint和已注册服务,不能直接注入RequestInterface等请求上下文对象 - 若需请求数据,应从
ProceedingJoinPoint的$joinPoint->getArguments()或上下文获取,而非属性注入
最易忽略的是:所有注入都依赖容器生命周期管理。协程环境下,单例作用域错配会导致数据跨请求污染;而没显式配置 scope 的服务,默认是单例,不是“每请求一份”。











