![](https://source.unsplash.com/random/1680x200) ## 依赖注入之-容器 设计架构图 ![](https://img.kancloud.cn/ca/ab/caabe11e642b4085e9c419117ba458cc_1080x780.png) 依赖注入(Dependency Injection,DI)容器就是一个对象,它知道怎样初始化并配置对象及其依赖的所有对象。 注册会用到一个依赖关系名称和一个依赖关系的定义。 依赖关系名称可以是一个类名,一个接口名或一个别名。 依赖关系的定义可以是一个类名,一个配置数组,或者一个 PHP 回调。 ### 一、常见注入方式 #### 1、构造方法注入(Constructor Injection) 在参数类型提示的帮助下,DI 容器实现了构造方法注入。当容器被用于创建一个新对象时, 类型提示会告诉它要依赖什么类或接口。 容器会尝试获取它所依赖的类或接口的实例, 然后通过构造器将其注入新的对象。例如: ``` class Foo { public function __construct(Bar $bar) { } } $foo = $container->get('Foo'); // 上面的代码等价于: $bar = new Bar; $foo = new Foo($bar); ``` #### 2、方法注入(Method Injection) 通常,类的依赖关系传递给构造函数,并且在整个生命周期中都可以在类内部使用。 通过方法注入,可以提供仅由类的单个方法需要的依赖关系, 并将其传递给构造函数可能不可行,或者可能会在大多数用例中导致太多开销。 ```php class MyClass extends \yii\base\Component { public function __construct(/*Some lightweight dependencies here*/, $config = []) { // ... } public function doSomething($param1, \my\heavy\Dependency $something) { // do something with $something } } ``` #### 3、Setter 和属性注入(Setter and Property Injection) Setter 和属性注入是通过[配置](https://www.yiichina.com/doc/guide/2.0/concept-configurations)提供支持的。 当注册一个依赖或创建一个新对象时,你可以提供一个配置, 该配置会提供给容器用于通过相应的 Setter 或属性注入依赖。 例如: ``` use yii\base\BaseObject; class Foo extends BaseObject { public $bar; private $_qux; public function getQux() { return $this->_qux; } public function setQux(Qux $qux) { $this->_qux = $qux; } } $container->get('Foo', [], [ 'bar' => $container->get('Bar'), 'qux' => $container->get('Qux'), ]); ``` ### 二、直接注入类到容器中 TP5.1 `绑定一个类到容器中(第一个参数直接传入类名)` **1、使用容器** ``` // 绑定类库标识 Container::getInstance()->bindTo(GameService::class); // 自动实例化 $obj = Container::get(GameService::class); // 快速调用 halt($obj->test()); ``` **2、使用助手函数** ``` // 绑定类库标识 bind(GameService::class); // 快速调用(自动实例化) $obj = app(GameService::class); halt($obj); ``` Yii2.0 `注册一个同类名一样的依赖关系` ``` $container = new \yii\di\Container; // 注册一个同类名一样的依赖关系,这个可以省略。 $container->set(GameService::class); ``` ### 三、绑定类标识 TP5.1 `可以对已有的类库绑定一个标识(唯一),便于快速调用` **1、使用容器** ``` // 绑定类库标识 Container::getInstance()->bindTo('game_service2',GameService::class); // 快速调用(自动实例化) $obj = Container::get('game_service2'); halt($obj); ``` **2、使用助手函数** ``` // 绑定类库标识 bind('game_service',GameService::class); // 快速调用(自动实例化) $obj = app('game_service'); // 带参数实例化调用 $obj = app('game_service',['file']); halt($obj->test()); ``` Yii2.0 `注册一个别名` ``` $container = new \yii\di\Container; $container->set('foo', 'yii\db\Connection'); // 创建一个 Connection 实例 $obj = $container->get('foo'); ``` ### 四、绑定闭包 TP5.1 使用 ``` // 使用助手函数 bind('sayHello', function ($name) { return 'hello,' . $name; }); echo app('sayHello',['thinkphp']); // 使用容器 Container::getInstance()->bindTo('sayTinywan',function ($age){ return 'Tinywan is '.$age; }); echo Container::get('sayTinywan',[24]); ``` Yii2.0 `注册一个 PHP 回调` ``` // 每次调用 $container->get('db') 时,回调函数都会被执行。 $container->set('db', function ($container, $params, $config) { return new \yii\db\Connection($config); }); ``` ### 四、类绑定到接口 ``` // 绑定think\LoggerInterface接口实现到think\Log bind('think\LoggerInterface','think\Log'); ``` 使用接口作为依赖注入的类型 ``` <?php namespace app\index\controller; use think\LoggerInterface; class Index { public function hello(LoggerInterface $log) { $log->record('hello,world!'); } } ``` 或者使用容器 ``` // 绑定 app\driver\DriverInterface接口实现到 app\driver\YoungDriver $driver = Container::getInstance()->bindTo(DriverInterface::class,YoungDriver::class); $car = Container::get(Car::class, [$driver]); var_dump($car->run()); ``` ## 依赖注入之-服务定位器 服务定位器是一个了解如何提供各种应用所需的服务(或组件)的对象。在服务定位器中, 每个组件都只有一个单独的实例,并通过ID 唯一地标识。 用这个 ID 就能从服务定位器中得到这个组件。 ### TP5.1 使用 **1、配置文件`provider.php`** 系统会自动批量绑定类库到容器中 ``` // 应用容器绑定定义 return [ 'game_player' => \app\common\repositories\player\Game::class, 'random' => \app\common\repositories\game\RandomGroup::class ]; ``` **2、那类玩家在玩那个游戏** ``` // 声明那类玩家 Container::getInstance()->bindTo(PlayerInterface::class,TeacherPlayer::class); // 要玩那个游戏 $game = app('random'); // 获取玩家实例 $player = app('game_player'); // 玩家开始玩什么游戏 halt($player->play($game)); ``` **3、携带参数** ``` // 声明那类玩家 Container::getInstance()->bindTo(PlayerInterface::class, StudentPlayer::class); // 要玩那个游戏 $game = app('random'); // 获取玩家实例 $parms = ['caode' => 200, 'name' => 'wangwang']; $player = app('game_player',['ddd']); // 玩家开始玩什么游戏 halt($player->play($game,$parms)); ```