Yii2 理解di
来源:互联网 发布:做电力行业的软件 编辑:程序博客网 时间:2024/05/17 23:41
- 链接
- 版本
- 简述
- Container
- Instance TOP
- 示例说明 TOP
0 链接
http://alex-my.xyz/web/Yii2-理解di
1 版本
// yii\BaseYii\getVersionpublic static function getVersion(){ return '2.0.10';}
2 简述
简单的说就是di把类的构造函数分析好存储起来,配上给定的参数,创建实例。
// 类名给定后通过php的反射机制获取构造函数信息// set的第二个参数是构造函数需要使用的参数值$container->set('yii\db\Connection', [ 'dsn' => 'mysql:host=127.0.0.1;dbname=demo', 'username' => 'root', 'password' => '123456', 'charset' => 'utf8', ]);
3 Container
\yii\di\Container
$_singletons, 保存单例键: 类名,接口名,别名值: 类的示例, null表示未初始化
$_definitions, 保存依赖定义键:类名,接口名,别名值:函数, 数组(一定要含有class元素)
$_params, 保存构造函数的参数键:类名,接口名,别名值:数组
$_reflections, 缓存类反射实例键:类名,接口名,别名值:类反射实例
$_dependencies, 缓存依赖信息键:类名,接口名,别名值:
normalizeDefinition处理依赖定义, 这里的$definition会影响到$class的构造函数参数值
返回一个数组,且数组中必须含有class元素
示例说明见set, setSingleton的后面
protected function normalizeDefinition($class, $definition){ // 则直接返回,$class构造函数没有参数 if (empty($definition)) { return ['class' => $class]; } // 如果是字符串,则认为$definition是所依赖的类名,接口名,或者别名 elseif (is_string($definition)) { return ['class' => $definition]; } // 认为$class是别名,$definition能得到一个对象实例 elseif (is_callable($definition, true) || is_object($definition)) { return $definition; } elseif (is_array($definition)) { // $definition除了class元素外,都是$class类构造函数的参数值 if (!isset($definition['class'])) { // 如果没有包含'class'元素,则$class一定是一个完整路径的类名/接口名 // 比如$class为yii\db\Connection if (strpos($class, '\\') !== false) { $definition['class'] = $class; } else { throw new InvalidConfigException("A class definition requires a \"class\" member."); } } return $definition; } else { throw new InvalidConfigException("Unsupported definition type for \"$class\": " . gettype($definition)); }}
set, setSingleton,注册依赖
前者注册的之后每次get都会得到新的实例
后者注册的之后每次get都是得到第一次实例化的对象
二者的区别将在get函数中体现
public function set($class, $definition = [], array $params = []){ $this->_definitions[$class] = $this->normalizeDefinition($class, $definition); $this->_params[$class] = $params; unset($this->_singletons[$class]); return $this;}public function setSingleton($class, $definition = [], array $params = []){ $this->_definitions[$class] = $this->normalizeDefinition($class, $definition); $this->_params[$class] = $params; $this->_singletons[$class] = null; return $this;}
使用示例, set, setSingleton, normalizeDefinition相关
函数原型:
normalizeDefinition($class, $definition)
// 在normalizeDefinition中,$class为真正的类名$container->set('yii\db\Connection');// 在normalizeDefinition中, $definition才是真正将来实例化的类$container->set('yii\mail\MailInterface', 'yii\swiftmailer\Mailer');// 相当于注册了一个别名foo, 因为$_definitions, $_params, $_singletons都是用$class做为键// 真正的class信息会存储在$definition['class']中$container->set('foo', 'yii\db\Connection');// $_definitions直接保存了$definition这些依赖信息($class构造函数的参数信息)$container->set('yii\db\Connection', [ 'dsn' => 'mysql:host=127.0.0.1;dbname=demo', 'username' => 'root', 'password' => '123456', 'charset' => 'utf8', ]);// 前面一个不方便使用,可以定义别名,可以使用$container->get('db')$container->set('db', [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=127.0.0.1;dbname=demo', 'username' => 'root', 'password' => '123456', 'charset' => 'utf8', ]);// $definition可以是函数,每次使用的时候都会调用这个函数生成新的实例$container->set('db', function ($container, $params, $config) { return new \yii\db\Connection($config); });// $definition可以是实例,每次调用都会使用这个实例, 相当于setSingleton$container->set('db', new \yii\db\Connection($config))
getDependencies($class)解析依赖信息,主要是获取类的构造函数的信息,这样才能调用构造函数创建实例
参数$class是类名
protected function getDependencies($class){ // 如果已有缓存的依赖信息,则直接使用 if (isset($this->_reflections[$class])) { return [$this->_reflections[$class], $this->_dependencies[$class]]; } $dependencies = []; // 使用PHP5的反射来获取类的信息 // 通过ReflectionClass,可以获取$class的以下信息: // 属性,函数,常量,静态属性,命名空间等 $reflection = new ReflectionClass($class); // 获得$class这个类的构造函数信息 $constructor = $reflection->getConstructor(); if ($constructor !== null) { // 解析构造函数的参数 foreach ($constructor->getParameters() as $param) { // 如果参数有默认值,则直接使用该默认值 if ($param->isDefaultValueAvailable()) { $dependencies[] = $param->getDefaultValue(); } // 用Instance封装参数, Instance::id存储着类名,在build中会进行实例化 else { $c = $param->getClass(); $dependencies[] = Instance::of($c === null ? null : $c->getName()); } } } // 缓存起来,供下次使用 $this->_reflections[$class] = $reflection; $this->_dependencies[$class] = $dependencies; return [$reflection, $dependencies];}
resolveDependencies实例化依赖,也就是创建构造函数的参数对象
protected function resolveDependencies($dependencies, $reflection = null){ foreach ($dependencies as $index => $dependency) { // 在解析依赖信息的getDependencies中,有部分参数没有默认值,而是创建了Instance对象 // 这里会将这些Instance对象实例化对真正的构造函数的参数对象 if ($dependency instanceof Instance) { if ($dependency->id !== null) { // 从di中获取真正的示例对象 $dependencies[$index] = $this->get($dependency->id); } ... } } return $dependencies;}
真正创建对象的是通过build, $class是类名,而不是别名,接口名
protected function build($class, $params, $config){ // 类的信息$reflection // 类的构造函数信息 list ($reflection, $dependencies) = $this->getDependencies($class); // 用$params的内容补充,覆盖到构造函数信息中 foreach ($params as $index => $param) { $dependencies[$index] = $param; } // 实例化构造函数中的参数 $dependencies = $this->resolveDependencies($dependencies, $reflection); // 不能实例化的类(为何不放前面...) if (!$reflection->isInstantiable()) { throw new NotInstantiableException($reflection->name); } // 没有构造函数,则直接实例化 if (empty($config)) { return $reflection->newInstanceArgs($dependencies); } // 如果是实现了接口Configurable,需要将配置放到构造函数参数列表的最后一个 if (!empty($dependencies) && $reflection->implementsInterface('yii\base\Configurable')) { $dependencies[count($dependencies) - 1] = $config; return $reflection->newInstanceArgs($dependencies); } else { $object = $reflection->newInstanceArgs($dependencies); foreach ($config as $name => $value) { $object->$name = $value; } return $object; }}
我们最终会用到的是get函数
public function get($class, $params = [], $config = []){ // 单例,且存在,则直接使用 if (isset($this->_singletons[$class])) { return $this->_singletons[$class]; } // 没有使用set/setSingleton注册的,直接创建实例 elseif (!isset($this->_definitions[$class])) { return $this->build($class, $params, $config); } $definition = $this->_definitions[$class]; if (is_callable($definition, true)) { // 将传入的参数和注册时填的参数合并,获取最终的构造函数的参数 $params = $this->resolveDependencies($this->mergeParams($class, $params)); // 调用函数获得实例 $object = call_user_func($definition, $this, $params, $config); } elseif (is_array($definition)) { // $class可能是类名,也可能是别名 // $concrete只能是类名 $concrete = $definition['class']; unset($definition['class']); $config = array_merge($definition, $config); $params = $this->mergeParams($class, $params); if ($concrete === $class) { // 如果二者相同,则直接创建实例 $object = $this->build($class, $params, $config); } else { // 如果$class是别名,则传入类名递归 $object = $this->get($concrete, $params, $config); } } // 如果直接给的是实例,则存为单例 elseif (is_object($definition)) { return $this->_singletons[$class] = $definition; } else { throw new InvalidConfigException('Unexpected object definition type: ' . gettype($definition)); } // 存为单例 if (array_key_exists($class, $this->_singletons)) { $this->_singletons[$class] = $object; } return $object;}
3 Instance TOP
\yii\di\Instance
用于存储Container中$class构造函数的参数,延迟实例化
$id, 组件id, 可以是类名,接口名,或者别名。
4 示例说明 TOP
该示例位于\yii\di\Container中
namespace app\models;use yii\base\Object;use yii\db\Connection;use yii\di\Container;interface UserFinderInterface{ function findUser();}class UserFinder extends Object implements UserFinderInterface{ public $db; public function __construct(Connection $db, $config = []) { $this->db = $db; parent::__construct($config); } public function findUser() { }}class UserLister extends Object{ public $finder; public function __construct(UserFinderInterface $finder, $config = []) { $this->finder = $finder; parent::__construct($config); }}$container = new Container;// 注册方法1,类名+参数$container->set('yii\db\Connection', [ 'dsn' => '...',]);// 注册方法2,接口名+类名$container->set('app\models\UserFinderInterface', [ 'class' => 'app\models\UserFinder',]);// 注册方法3,别名+类名$container->set('userLister', 'app\models\UserLister');// 使用$lister = $container->get('userLister');
TOP
0 0
- Yii2 理解di
- spring DI理解
- IoC-DI容器理解
- DI【理解】【应用】【重点】
- IoC/DI理解
- IOC/DI 理解整理
- 范例解说DI和DI容器 - 轻松理解DI
- Yii2 理解Object
- Yii2 理解Component
- Yii2 理解Controller
- Yii2 理解filters
- Yii2 理解Validator
- 深入理解 Yii2.0
- yii2框架-理解yii2的架构(一)
- Yii2框架-理解Yii2的架构
- 如何理解IoC和DI
- Spring IoC/DI的理解
- 关于IOC/DI的理解
- POJ 2114 点分治
- android_77_fragment_support向下兼容
- STM32引脚的4中输出模式
- 【SSH网上商城项目实战07】Struts2和Json的整合
- mina框架详解
- Yii2 理解di
- Forward vs Deferred vs Forward+ Rendering with DirectX 11
- PlayFramework有哪些让人眼前一亮却又不为人所知的特性
- OpenGL着色器语法写法注意
- 如何更加高效的学习
- 基于MFC的ActiveX控件的退出(卸载)
- Java四种线程池的使用
- 三国乐投晚评 1月12日
- JavaScript 的 concat() 方法的使用