Yii2中组件的注册与创建方法
今天本来打算研究一下yii2.0的AR模型的实现原理,然而,计划赶不上变化,突然就想先研究一下yii2.0的数据库组件创建的过程。通过对yii源码的学习,了解了yii组件注册与创建的过程,并发现原来yii组件注册之后并不是马上就去创建的,而是待到实际需要使用某个组件的时候再去创建对应的组件实例的。本文大概记录一下这个探索的过程。 要了解yii组件的注册与创建,当然要从yii入口文件index.php说起了,整个文件代码如下: run();
可以看到入口文件引入了几个配置文件,并将所有配置文件的内容都合并到$config这个配置数组中,然后使用这个配置数组作为参数去创建一个应用实例。若将这个配置数组打印出来,就会看到,“components”下标对应的元素包含了yii组件的参数信息(这里只截图一小部分): 这些组件的信息是在引入进来的几个配置文件中配置的,Yii组件就是使用这些参数信息进行注册与创建的。 接下来就进入yiiwebApplication类的实例化过程了,yiiwebApplication类没有构造函数,但是它继承了yiibaseApplication类: 所以会自动执行yiibaseApplication类的构造函数: state = self::STATE_BEGIN;
$this->preInit($config);
$this->registerErrorHandler($config);
Component::__construct($config);
}
这里要顺便说一下预初始化方法preInit(),它的代码如下: coreComponents() as $id => $component) {
if (!isset($config['components'][$id])) {
$config['components'][$id] = $component;
} elseif (is_array($config['components'][$id]) && !isset($config['components'][$id]['class'])) {
$config['components'][$id]['class'] = $component['class'];
}
}
}
这个函数对传递给构造函数的配置数组$config进行了一些预处理操作(这里省略了),最后使用coreComponents()方法返回的数组对$config数组进行了完善,coreComponents()方法是这样的: ['class' => 'yiilogDispatcher'],'view' => ['class' => 'yiiwebView'],'formatter' => ['class' => 'yiii18nFormatter'],'i18n' => ['class' => 'yiii18nI18N'],'mailer' => ['class' => 'yiiswiftmailerMailer'],'urlManager' => ['class' => 'yiiwebUrlManager'],'assetManager' => ['class' => 'yiiwebAssetManager'],'security' => ['class' => 'yiibaseSecurity'],];
}
其实就是一些核心组件的配置,也就是说这些组件是可以不需要我们在配置文件中配置的,yii会自动进行注册。 好了,回到yiibaseApplication类的构造函数,这个函数最后调用了yiibaseComponent类的构造函数,但yiibaseComponent类是没有构造函数的,不过它继承了yiibaseObject类: 所以也自动执行了yiibaseObject类的构造函数: init();}
这里主要是调用了yiiBaseYii类的静态方法configure(): $value) {
$object->$name = $value;
}
return $object;
}
这个方法就是循环入口文件 好了,现在就来看看setComponents()这个方法到底干了啥: $component) {
$this->set($id,$component);
}
}
其实很简单,就是循环各个组件的配置数组,调用set()方法,set()方法如下: _components[$id]);
if ($definition === null) {
unset($this->_definitions[$id]);
return;
}
if (is_object($definition) || is_callable($definition,true)) {
// an object,a class name,or a PHP callable
$this->_definitions[$id] = $definition;
} elseif (is_array($definition)) {
// a configuration array
if (isset($definition['class'])) {
$this->_definitions[$id] = $definition;
} else {
throw new InvalidConfigException("The configuration for the "$id" component must contain a "class" element.");
}
} else {
throw new InvalidConfigException("Unexpected configuration type for the "$id" component: " . gettype($definition));
}
}
其实就是把组件配置存入$_definitions这个私有成员变量(即注册),然后呢?然后就没有下文了。。。 搞了半天,原来yii创建应用实例的时候只是进行组件的注册,并没有实际创建组件,那么组件实例是什么时候进行创建的?在哪里进行创建的呢?别急。从上面推导的这个过程我们知道yiidiServiceLocator类是yiiwebApplication类的祖先类,所以其实yii的应用实例其实就是一个服务定位器,比如我们想访问数据库组件的时候,我们可以这样来访问:Yii::$app->db,这个Yii::$app就是yii应用实例,也就是yiiwebApplication类的实例,但是yiiwebApplication类和它的父类、祖先类都找不到db这个属性啊。哈哈,别忘了,php读取不到类属性的时候会调用魔术方法__get(),所以开始查找yiiwebApplication继承关系最近的祖先类中的__get()方法,最后在yiidiServiceLocator类中找到了,也就是说,Yii::$app->db最终会调用yiidiServiceLocator类中的__get()方法: has($name)) {
return $this->get($name);
} else {
return parent::__get($name);
}
}
__get()方法首先调用has()方法(这个不再贴代码了)判断组件是否已注册,若已注册则调用get()方法: _components[$id])) {
return $this->_components[$id];
}
if (isset($this->_definitions[$id])) {
$definition = $this->_definitions[$id];
if (is_object($definition) && !$definition instanceof Closure) {
return $this->_components[$id] = $definition;
} else {
return $this->_components[$id] = Yii::createObject($definition);
}
} elseif ($throwException) {
throw new InvalidConfigException("Unknown component ID: $id");
} else {
return null;
}
}
其中私有成员变量$_components是存储已经创建的组件实例的,若发现组件已经创建过则直接返回组件示例,否则使用$_definitions中对应组件的注册信息,调用yiiBaseYii::createObject()方法进行组件创建,这个方法最终会调用依赖注入容器yiidiContainer的get()方法,接着就是依赖注入创建对象的过程了,关于这个过程已经在我的上一篇博文中讲解过了,可以参考一下:yii2之依赖注入与依赖注入容器。 好了,yii组件注册与创建的整个过程就是这样的。最后总结一下,其实yii创建应用实例的时候只是进行了各个组件的注册,也就是将组件的配置信息存入yiidiServiceLocator类的私有成员变量$_definitions中,并没有进行实际创建,等到程序运行过程中真正需要使用到某个组件的时候才根据该组件在$_definitions中保存的注册信息使用依赖注入容器yiidiContainer进行组件实例的创建,然后把创建的实例存入私有成员变量$_components,这样下次访问相同组件的时候就可以直接返回组件实例,而不再需要执行创建过程了。yii的这个组件注册与创建机制其实是大有裨益的,试想一下,如果在应用实例创建的时候就进行所有组件的创建,将会大大增加应用实例创建的时间,用户每次刷新页面都会进行应用实例的创建的,也就是说用户每刷新一次页面都很慢,这用户体验就很不好了,而且很多情况下有很多组件其实是没有使用到的,但是我们还是花了不少时间去创建这些组件,这是很不明智的,所以yii的做法就是:先把组件参数信息保存起来,需要使用到哪些组件再去创建相应的实例,大大节省了应用创建的时间,同时也节省了内存,这种思路是很值得我们学习的! 总结以上所述是小编给大家介绍的Yii2中组件的注册与创建方法,希望对大家有所帮助。程序员遇到问题都会上(编程之家52php.cn)查找问题解答方法!如果觉得站点还不错,随手转发给程序员朋友一下! (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |