一、依赖注入
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
通俗一点来讲不是由自己内部 new 对象或者实例,通过构造函数,或者方法传入的都属于 依赖注入。
这里我们使用开发中经常使用到的功能发邮件,在日常开发中,肯定会遇到有俩个服务商的短信吧。
下面使用代码来演示:
1 |
|
这里插一个重要概念:服务提供者。
Laravel 服务提供者主要用来进行注册服务容器绑定(即注册接口及其实现类的绑定)。
再通俗一点,第一步是为了发邮件写了接口,接口的实现类写好了,那需要有地方来调用,服务提供者就是调用的类(这里的服务提供者是 UserMail),绑定和实例化应该是在这里完成。
这个时候,可以看到如果我们要更换服务商A的话,就需要去修改操作类。所以现在使用依赖注入来对操作类代码进行优化。
1 |
|
这样一看代码,再加上上面的解释,是不是很清晰明了。
二、反射机制
反射是指在PHP运行状态中,扩展分析PHP程序,导出或提出关于类、方法、属性、参数等的详细信息,包括注释。这种动态获取信息以及动态调用对象方法的功能称为反射API。
PHP 5 及以上版本中有 ReflectionClass ,报告了一个类的有关信息。
示例:
1 |
|
现在可以通过创建一个 make 方法,利用反射机制拿到 UserMail 的构造函数,进而得到构造函数的依赖参数,通过递归方式创建参数依赖,最后调用 newInstanceArgs() 方法创建实例。
1 |
|
注意,这里没有使用到 依赖注入 ,没有完全达到解偶。所以这样肯定是不合格的。这就引出了另外一个概念 Ioc容器。
三、Ioc 容器
Ioc—Inversion of Control,即“控制反转”,就是具有依赖注入功能的容器,是可以创建对象的容器,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
我们可以借助一个容器,提前把服务商对象、操作类都绑定到 Ioc 容器 中,如果后期需要修改服务商,也只需要修改 Ioc 容器 里的绑定记录就行了,就很好的解耦上面代码。具体实现思路
- Ioc 容器维护 binding 数组记录 bind 方法传入的键值对如:mail => MailA,userMail => UserMail
- 在 ioc -> make(‘UserMail’) 的时候,通过反射拿到 UserMail 的构造函数,拿到构造函数的参数,发现参数是 UserMail 的构造函数参数 mail, 然后根据 mail 得到 MailA。
- 然后通过反射机制创建 $mail = new MailA();
- 最后通过 newInstanceArgs() 再创建 new UserMail($mail)
1 |
|
Ï
四、Laravel 生命周期
Laravel 的生命周期可以分成四个阶段
- 注册自动加载程序(加载项目依赖)
- 创建应用实例(服务容器),并绑定内核到容器
- 处理请求,响应
- 结束,进行回调
在入口文件 index.php
文件中
1 |
|
接下来,我们来走一遍这个流程
4.1 注册自动加载程序(加载项目依赖)
现在 PHP 依赖于 Composer 包管理器,所以在入口文件引入由 Composer 包管理器自动生成的类,可以轻松注册并加载第三方包
1 |
|
4.2 创建应用实例(服务容器),并绑定内核到容器
在入口文件 index.php
中,
1 | /* |
现在让我们来看看 /bootstrap/app.php
这里面发生什么
1 |
|
怎么创建实例呢?
在 Illuminate\Foundation\Application
文件中,主要
1 |
|
4.2.1 setBasePath()
设置应用程序的基本路径
在这个 setBasePath()
方法中,调用了 bindPathsInContainer()
1 | protected function bindPathsInContainer() |
将路径填入到类的容器的共享实例 ($instance = []
)中去。
4.2.2 registerBaseBindings()
注册基本绑定
1 | protected function registerBaseBindings() |
这段代码,是将实例注入到容器中。
1 | static::setInstance($this); |
就是将 $this
复制给了父类中的静态变量 $instance
(当前的全局可用容器)
重点看一下
1 | $this->instance('app', $this); |
这也是父类的一个方法
1 | // Illuminate\Container\Container.php |
instance()
方法的作用就是绑定一个已有对象到容器中,这个对象在容器中共享并可以通过键获得。
4.2.3 registerBaseServiceProviders()
注册所有基本服务提供商
1 | protected function registerBaseServiceProviders() |
这一步注册了三个 ServiceProvider ,在注册过程中,每个 ServiceProvider 的 register()
都会被执行。
1 | /** |
这些 ServiceProviders 都是 Illuminate\Support\ServiceProvider
的子类,他接受一个 Application 对象作为析构函数参数,存储在实例变量 $app
中。
在上面代码的 register()
方法中, ServiceProvider 的 register()
都会被执行。
那我们来看看 Illuminate\Events\EventServiceProvider
这个类
1 |
|
上面代码将 Illuminate\Events\Dispatcher
对象以键 events
绑定到容器中,来负责实现事件的调度。
再来看看 Illuminate\Log\LogServiceProvider
1 | /** |
上面将创建 logger 对象以键 log
绑定到容器中,来负责实现记录日志行为。
最后看看 lluminate\Routing\RoutingServiceProvider
1 | /** |
这里以类似上面两个的方式注册了大量的路由方法到容器中。
4.2.4 registerCoreContainerAliases()
在容器中注册核心类的别名
1 | /** |
可以看到,这里注册大量的别名到容器中去,方便后续直接调用。
4.2.5 绑定内核
现在回到 bootstrap/app.php
,代码执行到这里,会看到
1 | // 绑定内核到容器 |
singleton()
这个函数,前一个参数是实际类名,后面则是类的 “别名”。
$app
对象声明了3个单例对象。注意,这里只是声明了,也就是只起了个 ”别名“。
再来到 bootstrap/index.php
,可以看到
1 | $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); |
按照惯例,我们来看看这里的代码
注意哦,因为上面绑定声明了,所以不是去找
Illuminate\Contracts\Http\Kerne
,而应该是App\Http\Kernel
1 |
|
一目了然,这个类定义了各种中间件数组。
4.3 处理请求,响应
请求实例化
在程序启动准备工作完成之后,就开始请求的实例化。
1 | $response = $kernel->handle( |
对于请求就是客户端发送的一个请求报文,这个对应着 Illuminate\Http\Request
类。请求的实例创建通过 Illuminate\Http\Request
中的 capture()
方法完成。
1 | /** |
1 | /** |
处理请求
实例化完成之后就是要对实例进行处理。
看回来 Illuminate\Foundation\Http\Kernel
,请求处理是通过 handle()
进行的。
1 | /** |
这里先不讲
route
部分,之后再深入
最后通过 dispatchToRouter()
响应
1 | /** |
然后以 public/index.php
中 send()
发送
1 | $response->send(); |
4.4 结束,释放资源
1 | $kernel->terminate($request, $response); |
1 | /** |
参考:
https://segmentfault.com/a/1190000014598466
https://www.jianshu.com/p/db2234e81afc
不对之处,请多指教