yii 2.0 中 yii::$app 是服务定位器(service locator),非 di 容器;需手动注册组件才能通过 get() 获取,不自动解析构造函数依赖;真正 di 需单独使用 yii\di\container 并提前配置。

Yii 2.0 里 Yii::$app 是服务定位器,不是 DI 容器
很多人以为 Yii::$app 能像 Laravel 的 app() 那样自动解析依赖,其实它只是个服务定位器(Service Locator):你得先手动注册服务,它才找得到;它不分析构造函数参数,也不递归实例化依赖链。
典型错误现象:Class 'SomeService' not found 或调用 Yii::$app->get('someService') 报错说未注册——这不是 DI 失败,是压根没配进去。
-
Yii::$app默认只预注册了'db'、'cache'、'request'等少数核心组件 - 自己写的类(比如
OrderService)必须显式配置到components数组里才能被Yii::$app->get()找到 - 配置时若用匿名函数返回实例,每次
get()都新建对象;若用'class' => OrderService::class,默认是单例(可配'singleton' => false改变)
想用真正 DI,得自己装容器(比如 yii\di\Container)
Yii 2.0 自带的 yii\di\Container 是标准 DI 容器,支持自动构造注入、接口绑定、作用域管理,但它和 Yii::$app 是两套体系,不互通。
常见误用:把 Container 实例塞进 Yii::$app->set('container', $container),然后指望 Yii::$app->get('SomeService') 能触发 DI 解析——不行,ServiceLocator 不会委托给 DI 容器。
- 要启用 DI,得直接 new
yii\di\Container,然后手动调用$container->get(OrderService::class) - 接口绑定写法:
$container->set('app\interfaces\OrderInterface', 'app\services\OrderService') - 构造函数参数含类型提示时,DI 容器能自动解析(前提是类已注册或可自动反射),
ServiceLocator完全做不到这点 - 注意性能:频繁 new
Container开销大,建议在应用启动时创建一个全局实例复用
components 配置里混用 DI 和 SL 容易踩坑
你在 config/web.php 的 'components' 里写 'orderService' => ['class' => OrderService::class],看起来像 DI,其实是 Service Locator 在做简单工厂模式——它只认 'class'、'singleton' 这几个固定 key,不处理 __construct 参数。
错误现象:类构造函数需要 DbConnection 和 CacheInterface,但配置里没传,启动就报 Missing argument——因为 SL 不解析参数,只调 new OrderService()。
- 解决方案一:在配置里手动补全依赖:
'orderService' => ['class' => OrderService::class, 'db' => Yii::$app->db, 'cache' => Yii::$app->cache] - 解决方案二:改用 DI 容器统一管理,把
components配置逻辑移到容器set()中,并用autowire或constructor显式指定参数 - 别在
components里写'class' => function () { return Yii::$container->get(OrderService::class); }——这等于绕开 SL 又没真正解耦,还容易引发循环依赖
升级到 Yii 3+ 才有原生融合的 DI
Yii 2.0 的 DI 和 SL 是并存但割裂的,文档也没明确划清边界,导致大量项目用着 SL 却幻想它有 DI 行为。实际开发中,如果你真需要构造注入、接口解耦、测试友好,就得主动引入 yii\di\Container 并绕过 Yii::$app 的组件访问路径。
最常被忽略的一点:DI 容器的配置(比如 set()、setSingleton())必须在 Yii::$app 初始化完成前执行,否则 Yii::$app->get() 拿不到你注册的服务——很多同学把容器配置写在控制器里,那就永远不生效。











