以下源码分析,我们可以从 App,Http 类的实例化过程,了解类是如何实现自动实例化的,依赖注入是怎么实现的。

从入口文件出发

当访问一个 ThinkPHP 搭建的站点,框架最先是从入口文件开始的,然后才是应用初始化、路由解析、控制器调用和响应输出等操作。
 
入口文件主要代码如下:
 

App 实例化

执行 new App() 实例化时,首先会调用它的构造函数。
 
 
构造函数实现了项目各种基础路径的初始化,并读取了 provider.php 文件,将其类的绑定并入 $bind 成员变量,provider.php 文件默认内容如下:
 
 
合并后,$bind 成员变量的值如下:
 
 
$bind 的值是一组类的标识到类的映射。从这个实现也可以看出,我们不仅可以在 provider.php 文件中添加标识到类的映射,而且可以覆盖其原有的映射,也就是将某些核心类替换成自己定义的类。
 
static::setInstance($this) 实现的作用,如图:
 
 
think\App 类的 $instance 成员变量指向 think\App 类的一个实例,也就是类自己保存自己的一个实例。
instance() 方法的实现:
 
 
其中的getAlias方法:
 
 
执行结果大概是这样的:
 

Http 类的实例化以及依赖注入原理

这里,$http = (new App())->http,前半部分好理解,后半部分乍一看有点让人摸不着头脑,App 类并不存在 http 成员变量,这里何以大胆调用了一个不存在的东东呢?
 
原来,App 类继承自 Container 类,而 Container 类实现了__get() 魔术方法,在 PHP 中,当访问到的变量不存在,就会触发__get() 魔术方法。该方法的实现如下:
 
 
实际上是调用get()方法:
 
 
然而,实际上,主要是make()方法:
 
 
然而,make()方法主要靠invokeClass()来实现类的实例化。该方法具体分析:
 
 
以上代码可看出,在一个类中,添加__make()方法,在类实例化时,会最先被调用。以上最值得一提的是bindParams()方法:
 
 
而这之中,又最值得一提的是getObjectParam()方法:
 
 
getObjectParam() 方法再一次光荣地调用 make() 方法,实例化一个类,而这个类,正是从 Http 的构造函数提取的参数,而这个参数又恰恰是一个类的实例 ——App 类的实例。到这里,程序不仅通过 PHP 的反射类实例化了 Http 类,而且实例化了 Http 类的依赖 App 类。假如 App 类又依赖 C 类,C 类又依赖 D类…… 不管多少层,整个依赖链条依赖的类都可以实现实例化。
 
总的来说,整个过程大概是这样的:需要实例化 Http 类 ==> 提取构造函数发现其依赖 App 类 ==> 开始实例化 App 类(如果发现还有依赖,则一直提取下去,直到天荒地老)==> 将实例化好的依赖(App 类的实例)传入 Http 类来实例化 Http 类。
 
这个过程,起个装逼的名字就叫做「依赖注入」,起个摸不着头脑的名字,就叫做「控制反转」。
 
这个过程,如果退回远古时代,要实例化 Http 类,大概是这样实现的(假如有很多层依赖):
 
 
这得有多累人。而现代 PHP,交给「容器」就好了。
 
另外,需要提的一点是 make 方法的 $vars 参数,它的形式可以是普通数组、关联数组,而且数组中元素的值可以是一个类的实例。$vars 参数的值最终将传递给要实例化的类的构造函数或者__make 方法中对应的参数。
 

Request 类的实例化

接上一面,得到Http类的一个实例后,程序接下来执行$response = $http->run();。其中run()方法代码如下:
 
 

从「request」标识找到要实例化的类

run() 方法的第一行通过容器类实例 app 调用 make() 方法并传入 Request 类的标识来实例化 Request 类。具体过程如下分析。
 
通过 make() 方法首先解析得到 request 标识对应的标识 think\Request, 进一步递归解析,又得到 app\Request 类 —— 这个才是最终要实例化的类。
 
app\Request 类对应的文件位于 app 目录下,代码如下:
 
 
实际上它啥事也没干,直接继承系统的 \think\Request。当然我们也可以在这里对系统的 Request 类进行改写重构。

调用 invokeClass () 方法

从类的标识解析得到最终需要实例化的类(单例模式下,且该类还不存在实例)之后,程序调用 invokeClass () 方法,通过 PHP 的反射类实现类的实例化。由于 \think\Request 类存在__make() 方法,所以实例化之前首先调用该方法。__make() 方法代码如下:
 
 
__make()方法首先实例化think\Request类自身。think\Request类构造函数如下:
 
 
构造函数读取了 php://input 保存起来。接着,__make() 方法保存了一些请求相关的数据,最后返回一个 Request 类实例。最后的最后, make() 方法也成功得到该实例,整个过程跟 Http 类的实例化类似。该 Request 类实例部分成员变量如图:
 

保存「Request」类的实例到「$instances」数组

得到 Request 类的实例后,run() 方法接着将该实例保存到「$instance」数组,以便后面单例模式要用到时可以直接获取。$instances 数组的值如图,Request 类的实例已保存在里面:
 
 
 

ThinkPHP6 核心分析之Http 类跟Request类的实例化的更多相关文章

  1. ThinkPHP6 核心分析之应用程序初始化

    runWithRequest () 方法 在 Http 类的 run() 方法中,得到 think\Request 类的实例后,程序接着执行 $response = $this->runWith ...

  2. ThinkPHP6 核心分析:系统服务

    什么是系统服务?系统服务是对于程序要用到的类在使用前先进行类的标识的绑定,以便容器能够对其进行解析(通过服务类的 register 方法),还有就是初始化一些参数.注册路由等(不限于这些操作,主要是看 ...

  3. drf03 drf视图中提供的请求类和响应类

    drf除了在数据序列化部分简写代码以外,还在视图中提供了简写操作.所以在django原有的django.views.View类基础上,drf封装了多个子类出来提供给我们使用. Django REST ...

  4. Thinkphp源码分析系列(二)–引导类

    在上一章我们说到,ThinkPHP.php在设置完框架所需要的变量和调教好环境后,在最后调用了  Think\Think::start();  即Think命名空间中的Think类的静态方法start ...

  5. JUnit4.8.2来源分析-2 org.junit.runner.Request

    JUnit4.8.2源代码,最为yqj2065兴趣是org.junit.runner.Request,现在是几点意味着它? ①封装JUnit的输入 JUnit4作为信息处理单元,它的输入是单元測试类- ...

  6. 分析Class类和ClassLoader类下的同名方法getResourceAsStream

    在读取本地资源的时候我们经常需要用到输入流,典型的场景就是使用Druid连接池时读取连接池的配置文件.Java为我们提供了读取资源的方法getResourceAsStream(),该方法有三种: Cl ...

  7. Java并发编程笔记之Unsafe类和LockSupport类源码分析

    一.Unsafe类的源码分析 JDK的rt.jar包中的Unsafe类提供了硬件级别的原子操作,Unsafe里面的方法都是native方法,通过使用JNI的方式来访问本地C++实现库. rt.jar ...

  8. Python爬虫连载3-Post解析、Request类

    一.访问网络的两种方法 1.get:利用参数给服务器传递信息:参数为dict,然后parse解码 2.post:一般向服务器传递参数使用:post是把信息自动加密处理:如果想要使用post信息,需要使 ...

  9. Spring源码分析——BeanFactory体系之抽象类、类分析(二)

    上一篇分析了BeanFactory体系的2个类,SimpleAliasRegistry和DefaultSingletonBeanRegistry——Spring源码分析——BeanFactory体系之 ...

随机推荐

  1. vs code 初始化vue项目框架

    1.首先安装npm组件  下载地址:https://nodejs.org/en/ 安装完 2.配置环境变量 3.验证是否成功 node -v npm -v 4.替换npm 输入npm install ...

  2. Python实用笔记 (7)高级特性——切片

    列表L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'] 取前3个元素 >>> L[0:3] ['Michael', 'Sarah', ' ...

  3. 恕我直言你可能真的不会java第9篇-Stream元素的匹配与查找

    在我们对数组或者集合类进行操作的时候,经常会遇到这样的需求,比如: 是否包含某一个"匹配规则"的元素 是否所有的元素都符合某一个"匹配规则" 是否所有元素都不符 ...

  4. 痞子衡嵌入式:kFlashFile v1.0 - 一个基于Flash的掉电数据存取方案

    大家好,我是痞子衡,是正经搞技术的痞子.今天给大家带来的是痞子衡的个人小项目 - kFlashFile. 痞子衡最近在参与一个基于 i.MXRT1170 的项目,项目有个需求,需要在 Flash 里实 ...

  5. 【数据库内核】RocksDB:事务锁设计与实现

    本文主要介绍 RocksDB 锁结构设计.加锁解锁过程,并与 InnoDB 锁实现做一个简单对比. 本文由作者授权发布,未经许可,请勿转载. 作者:王刚,网易杭研数据库内核开发工程师 MyRocks ...

  6. Cache写策略(Cache一致性问题与骚操作)

    写命中 写直达(Write Through) 信息会被同时写到cache的块和主存中.这样做虽然比较慢,但缺少代价小,不需要把整个块都写回主存.也不会发生一致性问题. 对于写直达,多出来%10向主存写 ...

  7. mongodb--创建用户权限

    最近在部署MongoDB Replica Set,马上就到生产环境了,一想还没有给数据库设置用户权限,配置的这一路踩了好多坑,希望对大家有帮助 1. 配置好mongodb replica set 安装 ...

  8. 小程序报错 parameter.content should be String instead of Undefined;

    自己遇到了两种情况会导致这个问题 1.参数名写错未定义,然后赋值的时候值为undefined 2.服务端返回的值错误,返回的值为空,导致赋值时报错 解决方法: 1.检查参数名,如不是全局变量的应在da ...

  9. Linux下diff命令用法详解

    大家好,我是良许. 我们在平时工作的时候,经常要知道两个文件之间,以及同个文件不同版本之间有何异同点.在 Windows 下,有 beyond compare 这个好用的工具,而在 Linux 下,也 ...

  10. Django之模型的_meta属性

    Python有反射机制,Django也不例外,也有很好的反射机制,每个Django模型都有一个属性_meta,_meta也有属性和方法,这些属性和方法反射出了模型的一些特性,如果_meta用的好的话, ...