Yii2的DI容器与服务定位器职责分明:Yii::$container负责按需构造对象及依赖链,Yii::$app仅管理components中显式注册的全局组件;绑定必须在对象创建前完成,推荐在config/web.php中声明definitions,接口绑定需写全命名空间。

Yii2 的依赖注入(DI)和服务容器不是配置完就自动生效的“魔法盒子”,它是一套有明确时机、层级和规则的机制。吃透它,关键在分清 谁管什么、什么时候配、怎么写才有效。
Yii::$container 是 DI 容器,Yii::$app 是服务定位器
这是最常混淆的起点。Yii::$container 负责按需构造任意对象及其依赖链,比如 new UserController() 时自动注入 UserService;而 Yii::$app 是服务定位器,只管理 components 配置里显式注册的全局组件(如 db、cache、mailer),通过 Yii::$app->db 访问。
两者默认指向同一个 Container 实例,但用途不同:
- 要用接口解耦(如 PaymentInterface → AlipayService),必须走 Yii::$container->set() 或 config 中的 container.definitions
- 要让某个类在构造时自动获得 UserService,靠的是容器解析,不是 Yii::$app->userService
- 改 Yii::$container 影响整个应用生命周期;改 Yii::$app->container 只影响当前 Application 实例(多应用隔离时有用)
绑定必须在对象创建前完成,时机错了等于没配
容器是“冻结式”工作的:启动时注册绑定,之后只解析不修改。常见失效场景就是把 set() 写在 action 里或 get() 之后。
- 推荐方式:在 config/web.php 的 'container' => ['definitions' => [...]] 中声明,框架会在初始化早期自动加载
- 手动方式:在 index.php 最顶部或 Application 构造前调用 Yii::$container->set(),确保早于任何 createObject() 或 new
- 写了 Yii::$container->set('Foo', 'Bar') 却没生效?先查是否被后续 new Foo() 绕过了容器——手动 new 永远不触发 DI
构造注入要过控制器这关,得靠 BaseController + 正确参数顺序
Yii2 控制器默认由框架用 new $class() 实例化,不走容器,所以不能直接在 SiteController 构造函数加 UserService $service 参数。
标准解法是定义基类并严格对齐父类签名:
- BaseController 构造函数形参顺序必须是 ($id, $module, $service1, $service2, $config = [])
- 每个自定义服务类型提示放在 $config 前,且必须显式调用 parent::__construct($id, $module, $config)
- 然后在 config 中配置 'controllerMap' 或确保所有控制器继承该基类,容器才能识别并注入
Setter 注入只响应显式键名,不扫描方法也不读 @property
Yii 不会自动发现 setLogger() 方法并注入,它只看你传的配置数组里有没有 'logger' 这个键。
- 配置 'logger' => ['class' => 'yii\log\Logger'] → 触发 setLogger()
- 配置 'apiUrl' => 'https://' → 触发 setApiUrl()(注意驼峰规则,不是 set_api_url)
- 类里只有 public $timeout; 没有 setTimeout()?配置 'timeout' => 5 会直接赋值给属性,破坏封装,不推荐
- @property User $user 这类 PHPDoc 纯属 IDE 提示,运行时完全忽略
接口绑定必须写全命名空间,字符串别简写
容器靠字符串精确匹配类型,漏掉 app\ 或写错大小写都会报 No entry or class found。
- 正确:
'app\interfaces\CacheInterface' => ['class' => 'app\components\FileCache'] - 错误:
'CacheInterface'、'interfaces\CacheInterface'、'App\Interfaces\CacheInterface'(大小写敏感) - 绑定具体类可简写:
'app\services\MailService' => 'app\services\MailService'


















