hyperf di容器是框架底层支撑,需理解绑定与解析机制:自动解析依赖需类有@service等注解或dependencies.php绑定,构造函数须有类型提示,接口需显式绑定;手动get()/make()仅用于非容器上下文、动态实例化或测试覆盖。

Hyperf 的 DI 容器不是“用一下就完事”的工具,而是整个框架运行的底层支撑——你几乎不用手动调用 get(),但必须理解它怎么绑定、怎么解析、为什么某些类无法自动注入。
容器默认如何自动解析类?
Hyperf 启动时会扫描 @Inject、@Value、构造函数参数等注解或类型提示,自动完成依赖注入。前提是目标类已注册(即被框架识别为可管理对象)。
常见误区:写了一个普通 PHP 类(没加 @Controller/@Service 等注解,也没在 dependencies.php 里声明),却指望它能被 $container->get(MyClass::class) 成功取出——这会抛出 NotFoundException。
- 类必须有明确的「可管理身份」:加
@Service注解,或继承AbstractService,或手动在dependencies.php中绑定 - 构造函数参数类型必须可解析:比如
public function __construct(LoggerInterface $logger)可行,但public function __construct($name)不行(无类型提示,容器不知道该塞什么) - 接口绑定需显式配置:如
LoggerInterface::class => StdoutLogger::class,否则默认找不到实现
什么时候必须手动调用 get() 或 make()?
绝大多数业务逻辑里不该手动取容器实例,但以下场景绕不开:
- 在非容器管理的上下文里获取服务,比如自定义命令的
handle()方法中,$this->container不可用,得用ApplicationContext::getContainer()->get(MyService::class) - 需要动态构造带参数的实例:用
make()而非get(),例如$container->make(HttpClient::class, ['base_uri' => 'https://api.example.com']) - 测试时临时覆盖依赖:在 PHPUnit 中通过
$container->set(...)替换真实实现
注意:get() 返回单例,make() 每次返回新实例;若类未绑定且无构造参数,make() 也能尝试反射创建,但带参数或依赖时仍需提前绑定。
dependencies.php 绑定写法与陷阱
这个文件是手动控制容器行为的核心入口,位于 config/autoload/dependencies.php。它不是“可选配置”,而是解决“接口多实现”“第三方类接入”“运行时决定绑定”的唯一可靠方式。
- 绑定接口到具体类:
StdoutLogger::class => LoggerInterface::class是错的,正确是LoggerInterface::class => StdoutLogger::class - 闭包绑定必须返回对象实例,不能只写 new:
function () { return new MyClient('v1'); }✅,new MyClient('v1')❌(后者在容器初始化时就被执行,无法延迟) - 避免循环引用:A 依赖 B,B 又依赖 A,容器会在解析时报
CircularDependencyException,需改用@Inject属性注入 +set延迟赋值,或重构依赖关系
为什么 @Inject 不生效?
最常被忽略的是作用域和时机问题:注解只在容器管理的对象中起作用。
- 控制器方法参数上的
@Inject无效——Hyperf 不支持方法级注入,只支持类属性或构造函数 - 在
__construct()里直接调用$this->xxx,但属性还没被注入(构造函数执行早于属性注入),应改用 setter 或延迟访问 - 使用了
new XxxService()手动实例化,完全绕过容器,所有@Inject属性保持 null - 类文件没被 Composer 自动加载(比如放在非
app/目录且未配置 autoload),容器根本扫描不到该类
调试技巧:用 var_dump(ApplicationContext::getContainer()->has(MyService::class)) 快速确认类是否已注册。











