## 依赖注入 (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` 指定的目录,App 会自动从该目录读取全部依赖配置),依赖配置是一个数组,其中每一个节点为一个 `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'); } ```