@inject仅支持public/protected属性注入,不支持方法参数或构造函数;必须配合@component等注册,且属性不可初始化赋值,否则跳过注入。

Inject注解必须配合属性声明使用,不能用于方法参数
Hyperf 的 @Inject 注解只支持类属性(property)注入,不支持构造函数或方法参数注入。如果你在方法签名里写 public function index(@Inject UserLogic $logic),会直接报错或静默失败——因为 DI 容器根本不会解析方法参数上的 @Inject。
常见错误现象:TypeError: Argument 1 passed to App\Controller\UserController::index() must be an instance of App\Service\UserLogic, null given,本质是没走容器注入流程。
- 必须用
#[Inject]修饰public或protected属性(PHP 8+ Attributes 推荐) - 属性类型提示必须与目标类完全一致(含命名空间),否则容器无法匹配
- 不支持
private属性,反射无法访问会导致注入失败 - 若类未被容器管理(比如没加
@Component或未自动扫描),注入值为null
类必须被容器识别,否则@Inject无效
@Inject 不是“自动创建实例”,而是从 DI 容器中取出已注册的服务。如果 UserLogic 类没被容器加载,注入结果就是 null,且无任何警告。
确认方式:执行 php bin/hyperf.php di:dump,检查输出中是否包含该类的完整类名(如 App\Service\UserLogic)。没有则说明未被扫描或注册。
- 确保类文件在
config/autoload/annotations.php的scan.paths范围内(如app/Service) - 类上需加
#[Component](推荐)或继承AbstractService,否则默认不注册进容器 - 不要依赖“只要用了就自动注册”——Hyperf 默认只扫描
Controller和Middlewares目录下的类 - 手动注册也可行:
$container->set(UserLogic::class, new UserLogic());,但破坏了自动装配逻辑
Inject和属性初始化顺序冲突会导致null
PHP 属性初始化表达式(= new Xxx() 或 = [])在 @Inject 注入之前执行。如果你写成:
#[Inject] protected UserLogic $logic = new UserLogic(); // ❌ 错误:$logic 已被赋值,注入被跳过
那么容器不会再覆盖该属性,最终你拿到的是手动 new 出来的实例,而非容器管理的单例,也无法享受 AOP、代理等特性。
- 正确写法是只声明类型,不赋值:
#[Inject] protected UserLogic $logic; - 如果需要默认值(比如可为空),用联合类型:
#[Inject] protected ?UserLogic $logic = null; - 切忌在构造函数中访问尚未注入的属性——
$this->logic此时仍为null
Inject不支持动态参数,复杂依赖需用工厂
@Inject 只能绑定一个具体类,无法传参(如 #[Inject("redis.default")] 是无效的)。如果你需要根据环境或配置选择不同实现,得靠工厂模式。
例如:要注入不同配置的 RedisClient,不能靠 @Inject 直接解决。
- 定义工厂类并标记
#[Component],在__invoke()中返回实例 - 在目标属性上用
#[Inject]注入该工厂,再调用它获取实例 - 或者改用
$container->get(RedisFactory::class)->make('default')手动取 - 别试图用
@Value+@Inject混合注入,@Value是配置项,不是服务实例
最易被忽略的一点:Hyperf 的 @Inject 是编译期静态绑定,不支持运行时条件判断。所有注入关系在容器构建阶段就确定了,后续无法动态替换——真要换实现,得靠接口 + 多个实现 + 条件绑定配置,而不是在控制器里 if-else 选 @Inject。











