- 什么是服务器容器
服务容器就是一个普通的容器,用来装类的实例,然后在需要的时候再取出来。用更专业的术语来说是服务容器实现了控制反转(Inversion of Control,缩写为 IoC),意思是正常情况下类 A 需要一个类 B 的时候,我们需要自己去 new 类 B,意味着我们必须知道类 B 的更多细节,比如构造函数,随着项目的复杂性增大,这种依赖是毁灭性的。控制反转的意思就是,将类 A 主动获取类 B 的过程颠倒过来变成被动,类 A 只需要声明它需要什么,然后由容器提供。
这样做的好处是,类 A 不依赖于类 B 的实现,这样在一定程度上解决了耦合问题。
在 Laravel 的服务容器中,为了实现控制反转,可以有以下两种:
依赖注入(Dependency Injection)。
绑定。
简而言之:A类不再直接依赖B类,而是在A类和B类之间生成一个缓冲区,利用这个缓冲区传递数据
简而言之:A类调用B类的方法的时候,不在A类中实例化一个B类的对象,而是只在函数中引入B类的对象(举例用构造函数),并在A类中保存后调用。而实例化B类的过程,放在了A类外部。
传统方法
clas A {
public function __construct() {
$this->obj = new B();
}
}
$a = new A()
依赖注入方法
clas A {
public function __construct(B $b) {
$this->obj = $b;
}
}
$b = new B();
$a = new A($b);
PS:PHP中为了解决依赖注入的一些问题,引入了接口类的概念(Interface)
1. 模块初始化(MINIT),即调用 php.ini 中指明的扩展的初始化函数进行初始化工作,如 mysql 扩展。
2. 请求初始化(RINIT),即初始化为执行本次脚本所需要的变量名称和变量值内容的符号表,如 $_SESSION 变量。
3. 执行该 PHP 脚本。(Lravel生命周期开始)
3.1 注册加载 composer 自动生成的 class loader,包括所有你 composer require 的依赖(对应代码 1).
3.2 生成容器 Container,Application 实例,并向容器注册核心组件。这里牵涉到了容器 Container 和合同 Contracts,以及依赖注入等问题
3.3 处理请求,生成并发送响应(对应代码 3,毫不夸张的说,你 99% 的代码都运行在这个小小的 handle 方法里面)。(具体代码的实现位置)
3.4 请求结束,进行回调(对应代码 4,还记得可终止中间件吗?没错,就是在这里回调的)。
4. 请求处理完成 (Request Shutdown),按顺序调用各个模块的 RSHUTDOWN 方法,对每个变量调用 unset 函数,如 unset $_SESSION 变量。
5. 关闭模块 (Module Shutdown) , PHP 调用每个扩展的 MSHUTDOWN 方法,这是各个模块最后一次释放内存的机会。这意味着没有下一个请求了。
PS:其他文档
使用 bind()/singleton() 方法把容器与接口具体的实现类进行绑定:
app()->bind(MyInterface::class, MyClass::class); // 使用一次绑定一次,下一次需要重新绑定
app()->singleton(MyInterface::class, MyClass::class); // 一个周期只绑定一次,下一次不需要绑定
$instance = app()->make(MyInterface::class);
// 相当于
$instance = app(MyInterface::class);
// 相当于
$instance = new MyClass(new AnotherClass());
绑定服务容器
interface Fruit
{
public function color();
}
class Apple implements Fruit
{
public $color;
public function __construct($color){
$this->color = $color;
}
public function color(){
return $this->color;
}
}
app()->bind('Fruit', 'Apple');
// 当类Apple需要$color参数的时候,将red传给他
app()->bind(Fruit::class, Apple::class);
app()->when(Apple::class)->needs('$color')->give('22');
echo app(Fruit::class)->color();
- 服务提供者Server Provider
用于绑定类到容器中
相当于将药片(类)放入药箱(容器)的小格子中(服务提供者),每个格子管理一种药片
// 新建一个服务提供者
php artisan make:provider AbcServiceProvider
解析顺序为:
// public/index.php
$app = require_once __DIR__.'/../bootstrap/app.php';// 注册核心服务
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
// bootstrap/app.php
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
// vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php,执行handle方法中的sendRequestThroughRouter
public function handle($request) {
...
$response = $this->sendRequestThroughRouter($request);
}
// 在sendRequestThroughRouter方法中再执行bootstrap()
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
// 因为执行的是根据config/app.php加载的providers,例如
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
所以要在config/app.php中注册AbcServiceProvider
之后在AbcServiceProvider中绑定类和容器
class AbcServiceProvider extends ServiceProvider implements DeferrableProvider
{
public function register()
{
$this->app->singleton(Connection::class, function ($app) {
return new Connection(config('riak'));
});
}
// 默认生成的方法是没有这个的
// 使用延时服务提供者后,只有在真正调用这个服务器提供者的时候,才会进行加载,节约内存
// 实现方法:implements DeferrableProvider和public function provides()和类名Connection::class
// 可以参考缓存的服务提供者vendor/laravel/framework/src/Illuminate/Cache/CacheServiceProvider.php
public function provides()
{
return [Connection::class];
}
}
加载外观服务有 AliasLoader 组件完成:
首先,会从配置文件 config/app.php 中读取所有的「外观」服务配置 aliases;
再从清单文件中读取别名服务 $app->make(PackageManifest::class)->aliases();
将两个配置数组合并后注入到 AliasLoader 完成 注册(先在使用「外观」服务时动态引入这个类后再进行注册。)
最后后在客户端中调用外观服务时,会调用实现「外观」的 getFacadeAccessor 方法获取到组件(服务或者说接口)的名称;然后从 Laravel 服务容器中解析出相关服务。