依赖注入
最后更新于:2022-04-02 07:05:21
## 依赖注入 (Bean)
MixPHP 参考了 Java Spring 的依赖注入(DI)与控制反转(IoC),具体实现就是通过 Bean 的方式,MixPHP 的实现是非常简单高效的,并且可独立使用,基于 [PSR-11](https://www.php-fig.org/psr/psr-11/) 标准实现。
## 安装
框架默认有安装
~~~
composer require mix/bean
~~~
## 依赖配置
负责描述依赖注入的结构关系,在 mix 里对应 manifest.php 文件的 `beans` 或者 `beanPath` 字段 (框架会遍历目录读取全部依赖配置),依赖配置是一个数组,其中每一个节点为一个 `bean` ,每个 `bean` 包含以下属性:
- name:依赖名称,默认等于 class 的值,通常只有在一个类需多次注入不同属性时使用,需给相同的类定义不同的名称。
- scope:作用域,值可为:BeanDefinition::PROTOTYPE 每次获取新的实例、BeanDefinition::SINGLETON 获取单例
- class:类的路径,通常使用 ::class 的方式获取
- properties:属性注入,配置为需注入的属性,内部的 key 将注入成类的属性名称,value 将赋值为属性的值。
- constructorArgs:构造参数注入,配置为需注入的参数,参数将依顺序传递给类的构造函数。
- ref:引用的另一个 `bean` 的 `name`
以下是一个日志类的依赖配置范例:
- 最终被调用的是 Logger 类,所以该类定义了 name,方便重写类功能时无需修改业务代码,只需修改依赖配置即可 ,其他类因为在当前程序中只有使用一次,所以都没有定义单独的 name ,因为默认 name 等于 class。
- 为了提升性能 Logger 类被定义为 BeanDefinition::SINGLETON 单例模式。
- 根据 Logger 类的源码可知,该类构造函数并没有强制要求传入参数,所以采用属性注入 (properties),而 MultiHandler 类源码只能通过构造函数实例化所以采用构造函数注入 (constructorArgs)。
- 范例一共定义了四个类,依赖关系都是通过 ref 来关联的,其中 Logger 类的 handler 属性注入了 MultiHandler 类的实例,而 MultiHandler 类实例初始化时注入了一个对象数组,对象数组为 StdoutHandler / FileHandler 两个类,而 FileHandler 类在实例化时又注入了一些配置参数。
~~~
// 依赖配置
'beans' => [
// 日志
[
// 名称
'name' => 'log',
// 作用域
'scope' => \Mix\Bean\BeanDefinition::SINGLETON,
// 类路径
'class' => \Mix\Log\Logger::class,
// 属性注入
'properties' => [
// 日志记录级别
'levels' => ['emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info', 'debug'],
// 处理器
'handler' => ['ref' => \Mix\Log\MultiHandler::class],
],
],
// 日志处理器
[
// 类路径
'class' => \Mix\Log\MultiHandler::class,
// 构造函数注入
'constructorArgs' => [
// 标准输出处理器
['ref' => \Mix\Log\StdoutHandler::class],
// 文件处理器
['ref' => \Mix\Log\FileHandler::class],
],
],
// 日志标准输出处理器
[
// 类路径
'class' => \Mix\Log\StdoutHandler::class,
],
// 日志文件处理器
[
// 类路径
'class' => \Mix\Log\FileHandler::class,
// 属性注入
'properties' => [
// 日志目录
'dir' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'runtime' . DIRECTORY_SEPARATOR . 'logs',
// 日志轮转类型
'rotate' => \Mix\Log\FileHandler::ROTATE_DAY,
// 最大文件尺寸
'maxFileSize' => 0,
],
],
],
~~~
## 通过依赖注入实例化
由于 mix 的命令行 App 启动时,已经在内部通过依赖配置信息初始化了一个 context 的属性,该属性为 Mix\Bean\ApplicationContext 的实例,负责 Bean 的调度:
```
// 获取context
$context = app()->context;
// 简化调用
$context = context();
```
因此以下方法即可在 mix 中通过依赖注入实例化一个类:
- 通过依赖名称获取类实例
```
$object = context()->get($beanName);
```
- 通过依赖名称获取类实例并传入参数
```
$params = [
'request' => $request,
'response' => $response,
];
$object = context()->getBean($beanName, $params);
```
## 类型注释
由于 PHP 是弱类型语言,因此通过依赖注入获取的实例并没有 IDE 的类型提示,因此我们需要借助 PHPDoc 来标注类型:
```
/** @var Logger $log */
$log= context()->get('log');
```
当然也可以把对象传值给类的属性,通过给类属性加 PHPDoc 来标注类型。
```
/**
* @var Logger
*/
public $log;
public function __construct()
{
$this->log = context()->get('log');
}
```
';