依赖注入(Dependency Injection,DI)容器就是一个对象,它知道怎样初始化并配置对象及其依赖的所有对象。注册会用到一个依赖关系名称和一个依赖关系的定义。依赖关系名称可以是一个类名,一个接口名或一个别名。依赖关系的定义可以是一个类名,一个配置数组,或者一个 PHP 回调。
在参数类型提示的帮助下,DI 容器实现了构造方法注入。当容器被用于创建一个新对象时, 类型提示会告诉它要依赖什么类或接口。容器会尝试获取它所依赖的类或接口的实例, 然后通过构造器将其注入新的对象。例如:
class Foo
{
public function __construct(Bar $bar)
{
}
}
$foo = $container->get('Foo');
// 上面的代码等价于:
$bar = new Bar;
$foo = new Foo($bar);
通常,类的依赖关系传递给构造函数,并且在整个生命周期中都可以在类内部使用。通过方法注入,可以提供仅由类的单个方法需要的依赖关系, 并将其传递给构造函数可能不可行,或者可能会在大多数用例中导致太多开销。
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
}
}
Setter 和属性注入是通过配置提供支持的。当注册一个依赖或创建一个新对象时,你可以提供一个配置, 该配置会提供给容器用于通过相应的 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 就能从服务定位器中得到这个组件。
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));