@Inject仅在容器管理的对象上生效,手动new的对象完全绕过DI流程,属性注入为null,构造函数类型提示也不解析,AOP代理、生命周期钩子等全部失效。

@Inject 只在容器管理的对象上生效,手动 new 的对象完全绕过 DI 流程
Hyperf 的依赖注入不是语言级特性,而是由 Container 在对象创建阶段主动介入完成的。当你写 new UserService(),PHP 直接调用构造函数、分配内存、返回实例——整个过程容器根本不知道,更不会去扫描 @Inject 属性、解析类型、注入依赖。
- 容器只对通过
$container->get(UserService::class)或$container->make(UserService::class)创建的对象执行注入逻辑(包括构造函数参数解析 +@Inject属性赋值) - 手动 new 出来的对象,其属性哪怕标了
@Inject,也永远是null;构造函数里写的类型提示也不会被容器解析 - 这种对象不进入生命周期管理:
@OnWorkerStart、AOP 代理、单例缓存等全部失效
为什么构造函数注入看起来“能用”,但其实也不可靠
如果一个类同时有 @Inject 属性和带类型提示的 __construct,Hyperf 默认优先走构造函数注入 —— 但这仅在该类本身由容器创建时才成立。一旦你手动 new,连构造函数里的类型提示都不会被处理,PHP 就按普通对象初始化对待,报错或传 null 都可能。
- 错误示例:
$user = new UserService(); // 构造函数参数全为 null,不报错但逻辑崩 - 正确姿势:必须统一走容器入口,哪怕只是临时用一次:
$user = $container->make(UserService::class); - 注意:
make()每次新建实例,get()才复用单例(前提是类注册为@Singleton或配置了singleton())
常见误用场景:Factory 类里 new 出来再返回
很多人写工厂方法时习惯内部 new 实例然后 return,比如:
<pre class="brush:php;toolbar:false;">class UserFactory
{
public function create(): UserService
{
return new UserService(); // ❌ 这里就断掉了注入链
}
}
结果是调用方拿到的 UserService 实例,所有 <code>@Inject 属性都是 null,后续调用直接报错。
- 修复方式:工厂内部改用
$container->get()或$container->make() - 更推荐做法:把工厂本身也交给容器管理,并在构造函数中注入所需依赖,避免硬编码 new
- 若工厂需动态参数(如 ID),应使用
make()并传入参数数组,而非 new
容易忽略的关键点:AOP 代理也依赖容器创建路径
如果你的类加了切面注解(比如 @Trace、@Retry),Hyperf 会生成代理类并由容器返回代理实例。但手动 new 绕过了这整条链 —— 既没注入,也没代理,日志、重试、监控全失效。
- 验证方式:检查
runtime/container/proxy/下是否有对应类的代理文件;没有,说明根本没进 AOP 流程 - 调试技巧:在构造函数里打日志,对比
$container->get()和new的输出,能立刻看出是否触发了完整初始化 - 最稳妥的习惯:项目中禁止出现
new XxxService(),全部走容器获取


















