Laravel 服务提供者是如何注册的
1. 服务提供者配置:
服务提供者是Laravel核心配置,在项目的config/app.php文件中的providers数组中,配置了启动应用所要加载的所有服务提供者类,其中很多是延迟加载的,也就是说不是每次请求都会被加载,只有真的用到它们的时候才会加载。这样的服务提供者类中有$defer决定。$defer=true。该服务提供者表示延迟加载。
/**
* 服务提供者加是否延迟加载.
*
* @var bool
*/
protected $defer = true;
2. 服务提供者主要方法:
register: 绑定事物到容器(本文大篇幅分析register的执行过程)
boot: 调用被注册的服务提供者,实现具体功能
3. 注册服务提供者流程
3.1 注册流程的过程大概
-
3.1.1 入口文件index.php开始执行代码
-
3.1.2 引入自动加载文件脚本
// 查看该autoload.php文件,引入了composer安装的vendor的自动加载文件
require DIR.'/../bootstrap/autoload.php'; -
3.1.3 引入应用程序,在应用程序中实例化了应用中心类Application,执行了一下初始化操作。其中包括事件,日志和路由的服务提供者的注册
$app = require_once DIR.'/../bootstrap/app.php'; -
3.1.4 获取laravel内核Kernel类实例
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); -
3.1.5 执行Kernel类中的handle方法处理HTTP请求,并返回数据。执行此方法的之前,获取了Request的实例,并作为参数传进来。Kernel作为Laravel的内核部分。接受HTTP请求,引用应用程序处理HTTP请求,最后返回Reponse
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
3.2 启动应用时,容器的初始化详情
-
3.2.1 创建Application实例
Application是Laravel的应用中心并且继承了容器Container类
$app = new Illuminate\Foundation\Application(
realpath(DIR.'/../')
); -
3.2.2 构造函数初始化
public function __construct($basePath = null) { if ($basePath) { // 设置应用基本路径 $this->setBasePath($basePath); } // 注册基本绑定 $this->registerBaseBindings(); // 注册基本服务提供者,包括事物,日志和路由 $this->registerBaseServiceProviders(); // 注册核心的容器别名 $this->registerCoreContainerAliases(); }
设置应用基本路径:
注册基本绑定
注册基本服务提供者,包括事物,日志和路由
注册核心的容器别名
- 3.2.3 注册基本服务提供者。用一种委托模式,将需要注册的三个服务提供者实例化,作为参数传进Application中的register方法
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));
$this->register(new LogServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
}
Application中的register方法是该委托模式下,执行传进来的服务提供者实例中自身的register方法。进而实现注册
public function register($provider, $options = [], $force = false)
{
// 检查当前服务提供者实例已经注册则直接返回
if (($registered = $this->getProvider($provider)) && ! $force) {
return $registered;
}
// 如果当前$provider是一个类名的字符串,则实例化该类
if (is_string($provider)) {
$provider = $this->resolveProvider($provider);
}
// 如果当前实例中存在register方法则调用该方法
if (method_exists($provider, 'register')) {
$provider->register();
}
// 标记服务提供者已注册状态
$this->markAsRegistered($provider);
if ($this->booted) {
$this->bootProvider($provider);
}
return $provider;
}
- 3.2.4 RoutingServiceProvider()中registert方法完成具体注册操作
public function register()
{
$this->registerRouter();
$this->registerUrlGenerator();
$this->registerRedirector();
$this->registerPsrRequest();
$this->registerPsrResponse();
$this->registerResponseFactory();
}
共享注册路由服务相关的各个实例绑定到容器。主要是通过app中的singleton方法来实现
singleton方法的功能是注册实例,绑定到容器
调用该方法通常会传两个参数:$abstract, $concrete。通过容器中的bind()方法,将这两个参数以key-value的形式绑定到容器中的$bindings数组中。之后需要用到通过此方法绑定的实例。可以通过make方法获取
3.3 绑定一些重要的接口。共享到容器
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
3.4 获取内核Kernel的实例
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
3.2 中提到singleton方法的作用,此处获取的Kernel实例就是在3.3步中绑定的重要接口之一
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
3.5 处理HTTP请求
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
执行App\Http\Kernel类中的handle方法。查看App\Http\Kernel类是继承了Illuminate\Foundation\Http\Kernel类,该类中定义了handle方法,用于处理传进来的HTTP请求
public function handle($request)
{
try {
$request->enableHttpMethodParameterOverride();
$response = $this->sendRequestThroughRouter($request);
} catch (Exception $e) {
$this->reportException($e);
$response = $this->renderException($request, $e);
} catch (Throwable $e) {
$this->reportException($e = new FatalThrowableError($e));
$response = $this->renderException($request, $e);
}
event(new Events\RequestHandled($request, $response));
return $response;
}
查看代码,执行Kernel中的sendRequestThroughRouter方法,通过中间件或是路由发送传进来的请求到处理程序
protected function sendRequestThroughRouter($request)
{
$this->app->instance('request', $request);
Facade::clearResolvedInstance('request');
$this->bootstrap();
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}
在sendRequestThroughRouter方法中执行了bootstrap方法,这个方法就是我们要找的,即引导应用程序来处理HTTP请求
public function bootstrap()
{
// 判断应用程序之前是否已被引导
if (! $this->app->hasBeenBootstrapped()) {
// 如何没有被引导,则引导程序注册应用程序需要被引导的数组
$this->app->bootstrapWith($this->bootstrappers());
}
}
其中注册所有服务提供者的引导类就在该$this->bootstrappers()方法返回的数组中
protected $bootstrappers = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
即,\Illuminate\Foundation\Bootstrap\RegisterProviders::class
3.6 注册服务提供者
通过Application中的bootstrapWith方法执行各个引用类的bootstrap方法。注册服务提供者即在此被执行
//Illuminate\Foundation\Bootstrap\RegisterProviders
public function bootstrap(Application $app)
{
$app->registerConfiguredProviders();
}
调用app中的registerConfiguredProviders注册所有服务提供者
public function registerConfiguredProviders()
{
(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
->load($this->config['app.providers']);
}
load方法加载config/app.php配置文件中的providers数组的内容。查看其内容,找到$this->app->register($provider)。将providers数组中的各个服务提供者完成注册。
在app的register()方法中
// 标记服务提供者已注册状态
$this->markAsRegistered($provider);
protected function markAsRegistered($provider)
{
$this->serviceProviders[] = $provider;
$this->loadedProviders[get_class($provider)] = true;
}
将所有服务提供者注册完成之后,保存在$serverProviders数组中,等待引导方法,即boot()方法。调用被注册过的服务提供者,实现具体功能