Yii3.0依赖注入容器是纯正契约优先的IoC容器,强制显式注册服务,支持接口绑定、闭包工厂和实例直传三种注册方式,并严格区分瞬态与单例生命周期,通过构造函数自动递归解析依赖,提升解耦性与可测试性。

在 Yii3.0 应用中实现组件解耦、提升可测试性与运行时动态配置能力,必须掌握其依赖注入容器(IoC Container)的注册、解析与生命周期控制机制。
理解 Yii3.0 容器的本质定位
Yii3.0 的 yii\di\Container 不再是 Yii2 中“服务定位器 + 轻量 IOC”的混合体,而是纯正的、契约优先的 IoC 容器。它默认禁用隐式类名解析,不自动尝试 new 未注册的类——这意味着:未显式绑定的类型,调用 get() 会直接抛出异常,而非静默创建新实例。
这一步设计强制开发者声明依赖关系,杜绝了 Yii2 中常见的“看似单例实为多次新建”的隐蔽陷阱。
注册服务的三种方式
方法一:接口绑定(推荐,面向契约)
调用 $container->set(LoggerInterface::class, FileLogger::class),后续任何构造函数中 type-hint LoggerInterface 的类,都将自动注入 FileLogger 实例。
方法二:闭包工厂(需运行时计算依赖)$container->set(DbConnection::class, fn($c) => new DbConnection($c->get(Config::class)['db']))。注意:闭包参数 $c 是容器自身,可用于递归解析其他依赖。
方法三:实例直传(仅用于已初始化对象)$container->setSingleton(Request::class, Yii::$app->getRequest())。该写法【必须配合 setSingleton】,否则下次 get() 仍会重新执行闭包或新建实例。
控制对象生命周期
Yii3.0 明确区分两种生命周期策略:
① 瞬态(Transient):每次 get() 都返回全新实例。
默认行为。无需额外操作,只要用 set() 注册即为瞬态。
② 单例(Singleton):整个请求生命周期内只创建一次,后续 get() 均返回同一实例。
必须显式调用 setSingleton(),例如:$container->setSingleton(Cache::class, ArrayCache::class)。
⚠️ 提醒:若误用 set() 注册本应单例的服务(如数据库连接),将导致连接池泄漏或事务上下文丢失。
构造函数注入实战流程
第一步:定义接口与实现interface PaymentGateway { public function charge(float $amount): bool; }class StripeGateway implements PaymentGateway { public function __construct(private HttpClient $client) {} ... }
第二步:在容器中完成两级绑定$container->set(HttpClient::class, GuzzleClient::class);$container->set(PaymentGateway::class, StripeGateway::class);
第三步:在业务类中声明依赖class OrderService { public function __construct(private PaymentGateway $gateway) {} }
容器会自动递归解析:先创建 GuzzleClient,再将其注入 StripeGateway 构造函数,最后将该网关实例注入 OrderService。
这一步不需要手动 new 任何对象,也不需要在类内部访问容器——彻底切断类与具体实现的耦合。
调试依赖解析失败
当出现 Target [App\Services\PaymentGateway] is not instantiable 错误时,按顺序检查:
→ 是否已调用 set() 或 setSingleton() 绑定该接口或类?
→ 绑定的目标类(如 StripeGateway)是否具有可被反射访问的构造函数?
→ 构造函数中每个参数类型(如 HttpClient)是否也已在容器中完成绑定?
→ 若参数为标量(string $apiKey),Yii3.0 默认拒绝注入,必须改用 with() 方法传参:$container->set(StripeGateway::class)->with(['$apiKey' => 'sk_test_...'])。


















