网站首页 > 博客文章 正文
TP6依赖注入是如何实现的
先看下app/provider容器文件,此文件会在think\APP实例化的时候
直接从新绑定类到的容器上。复制原有容器中的类
可以先看下think\APP 构造方法中的处理逻辑
/**
* 架构方法
* @access public
* @param string $rootPath 应用根目录
*/
public function __construct(string $rootPath = '')
{
//设置thinkphp扩展的目录
$this->thinkPath = dirname(__DIR__) . DIRECTORY_SEPARATOR;
//项目更目录
$this->rootPath = $rootPath ? rtrim($rootPath, DIRECTORY_SEPARATOR
//应用根目录
$this->appPath = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
//runtime根目录
$this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATO
//检测app/provider.php文件进行替换容器绑定
if (is_file($this->appPath . 'provider.php')) {
$this->bind(include $this->appPath . 'provider.php');
}
//将当前容器实例保存到成员变量「$instance」中,也就是容器自己保存自己的一个
static::setInstance($this);
//// 保存绑定的实例到「$instances」数组中,见对应分析
$this->instance('app', $this);
$this->instance('think\Container', $this);
}
在控制中可以使用app()->db 可以看到think\App中根本没有此属性,php的类中,调用一个类的不存在的属性就会自动进入魔术方法__get(),再来看看app类当中的__get方法,app类中没有找到集成的类中也就是think\Container 中直接搜索__get方法,就能找到。
think\Container
//$name就是就是没有定义的属性的名称
public function __get($name)
{
return $this->get($name);
}
找到当前类的get方法,首先是检查了以下容器中有没有,没有就直接实例化,进行调用make方法进行创建类的实例化。
/**
* 获取容器中的对象实例
* @access public
* @param string $abstract 类名或者标识
* @return object
*/
public function get($abstract)
{
if ($this->has($abstract)) {
return $this->make($abstract);
}
throw new ClassNotFoundException('class not exists: ' . $abstract, $a
}
make主要检测有没实例化过由实例化过后就直接返回使用就行,没有就需要利用类的反射来创建类的实例化,可以看到调用了本类的invokeClass方法
public function make(string $abstract, array $vars = [], bool $newInstance =
{
//如果已经存在实例,且不强制创建新的实例,直接返回已存在的实例
if (isset($this->instances[$abstract]) && !$newInstance) {
return $this->instances[$abstract];
}
//如果有绑定,比如 'http'=> 'think\Http',则 $concrete = 'think\Http'
if (isset($this->bind[$abstract])) {
$concrete = $this->bind[$abstract];
if ($concrete instanceof Closure) {
$object = $this->invokeFunction($concrete, $vars);
} else {
//重走一遍make函数,比如上面http的例子,则会调到后面「invokeClass
return $this->make($concrete, $vars, $newInstance);
}
} else {
//实例化需要的类,比如'think\Http'
$object = $this->invokeClass($abstract, $vars);
}
if (!$newInstance) {
$this->instances[$abstract] = $object;
}
return $object;
}
invokeClass方法主要为了绑定参数然后进行实例化类,绑定参数由bindParams方法实现,而bindParams方法中的getObjectParam方法中又会回调make方法。
/**
* 调用反射执行类的实例化 支持依赖注入
* @access public
* @param string $class 类名
* @param array $vars 参数
* @return mixed
*/
public function invokeClass(string $class, array $vars = [])
{
try {
//通过反射实例化类
$reflect = new ReflectionClass($class);
//检查是否有「__make」方法
if ($reflect->hasMethod('__make')) {
$method = new ReflectionMethod($class, '__make');
//检查是否是公有方法且是静态方法
if ($method->isPublic() && $method->isStatic()) {
//绑定参数
$args = $this->bindParams($method, $vars);
//调用该方法(__make),因为是静态的,所以第一个参数是null
//因此,可得知,一个类中,如果有__make方法,在类实例化之前会首
return $method->invokeArgs(null, $args);
}
}
//获取类的构造函数
$constructor = $reflect->getConstructor();
//有构造函数则绑定其参数
$args = $constructor ? $this->bindParams($constructor, $vars) : [
//根据传入的参数,通过反射,实例化类
$object = $reflect->newInstanceArgs($args);
// 执行容器回调
$this->invokeAfter($class, $object);
return $object;
} catch (ReflectionException $e) {
throw new ClassNotFoundException('class not exists: ' . $class, $
}
}
getObjectParam方法中是拿到当前类实例化的参数,找到当前参数是否是类,如果是就会直接再次调用make方法,如果下个参数还是个类的实例化结果,会再次进行回调,这就是一个类中可以无限制的注入多个类的原理,所以在使用的当中运用app()->make()来进行获取类的例化,更加方便简洁
/**
* 获取对象类型的参数值
* @access protected
* @param string $className 类名
* @param array $vars 参数
* @return mixed
*/
protected function getObjectParam(string $className, array &$vars)
{
$array = $vars;
$value = array_shift($array);
if ($value instanceof $className) {
$result = $value;
array_shift($vars);
} else {
$result = $this->make($className);
}
return $result;
}
总的来说,整个过程大概是这样的:需要实例化 Db 类 ==> 提取构造函数发现其依赖App 类==> 开始实例化 App 类(如果发现还有依赖,则一直提取下去,直到依赖全部加载完)==>将实例化好的依赖(App 类的实例)传入 Db类来实例化 Db类。
感谢您的阅读,如果对您有帮助,欢迎关注"CRMEB"头条号。码云上有我们开源的商城项目,知识付费项目,均是基于PHP开发,学习研究欢迎使用,关注我们保持联系!
猜你喜欢
- 2024-09-12 世界上最好的编程语言PHP图层裁剪服务搭建详解
- 2024-09-12 php之多级目录下查找文件中是否含有某个字符串功能实现
- 2024-09-12 phpcms v9类别调用方法(php class 调用)
- 2024-09-12 某设备产品漏洞挖掘-从JS文件挖掘RCE
- 2024-09-12 PHP页面缓存简单实现(php页面缓存简单实现方法)
- 2024-09-12 Laravel 创建自己的扩展包 package
- 2024-09-12 php 一步步实现mvc架构——view篇
- 2024-09-12 php写的mysql备份恢复的类(php备份mysql数据库)
- 2024-09-12 29、php类加载器实现(php类自动加载)
- 2024-09-12 轻松搭建基于 Serverless 的 ThinkPHP 应用
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- powershellfor (55)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)