Lumem(二):生命周期、容器、ServiceProvider

Lumen是基于Laravel框架,专注于后端微服务、无状态API开发的框架。
Laravel生命周期有完整的文档描述:Laravel Request lifecycle
Lumen官方缺少对其完成生命周期的文档说明,故展开说明下。

Lumen lifecycle生命周期

从源码依次入手,逐级跟踪调用关系,列表如下:

  • Lumen程序入口:public/index.php
    • 加载bootstrap/app.php:$app = require __DIR__.'/../bootstrap/app.php';
    • 执行容器的run():$app->run();
  • 文件bootstrap/app.php中所进行操作:
    • 加载vendor目录自动加载文件:require_once __DIR__.'/../vendor/autoload.php';, 这是composer默认自动加载文件。
    • 加载.env配置:(new Dotenv\Dotenv(__DIR__.'/../'))->load(),Lumen采用.env接管配置,此处进行配置加载。
    • 生成容器$app:$app = new Laravel\Lumen\Application(), 其实Laravel/Lumen的核心就是容器,再看Application源码也是继承了Container。
    • 启动Facades特性:$app->withFacades();, Lumen默认是禁用了Facades特性,取消注释则启用。
    • 启用Eloquent特性:$app->withEloquent();, Lumen默认禁用了Eloquent的ORM支持,作者说是给用户自行选择不同ORM的权利。
    • 加载配置文件:$app->configure('app');, 如果有自定义的配置文件,则需要在此进行加载。
    • 注册容器绑定:$app->singleton(), 看过源码singleton和bind区别就是前者内部调用了bind,且第三个参数设为true,意思是共享、独一的对象,也就是单例模式。
    • 注册中间件:$app->middleware, 根据需要添加或去除注释启用。
    • 注册ServiceProvider:$app->register(App\Providers\LogServiceProvider::class);, Laravel/Lumen的很大一部分特性是依赖ServiceProvider实现的。
    • 加载路由文件:$app->group(), 这里可以再跟下group()的实现,在vendor/laravel/lumen-framework/src/Concerns/RoutesRequests.php中。
    • 返回容器$app
      整个过程描述图示如下:
      Lumen lifecycle

: 这里用到了PHP trait,参考PHP oop Traits

Laravel/Lumen 容器/ServiceProvider

Laravel facades

Facades 模式

中文译名:外观模式、门面模式。
主要特性:屏蔽内部实现,开放接口供外部使用,提高模块抽象。
Laravel中的Facades也是在框架级提供抽象调用,从而使开发者无需关心内容实现。
Facades的理解:

  1. 别名,首先通过class_alias将DB、Log、Cache等关键词映射为不同的Facade类。

    1
    class_alias('Illuminate\Support\Facades\Cache', 'Cache');
  2. 不同Facade子类返回容器内注册服务的关键词: db、log、cache等。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Cache extends Facade
    {
    /**
    * Get the registered name of the component.
    *
    * @return string
    */
    protected static function getFacadeAccessor()
    {
    return 'cache';
    }
    }
  3. 通过不同世纪的绑定函数,向容器中注册或绑定service provider.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    protected function registerCacheBindings()
    {
    $this->singleton('cache', function () {
    return $this->loadComponent('cache', 'Illuminate\Cache\CacheServiceProvider');
    });
    $this->singleton('cache.store', function () {
    return $this->loadComponent('cache', 'Illuminate\Cache\CacheServiceProvider', 'cache.store');
    });
    }

Facades主要依赖Service Container和Service Provider实现。
注1:推荐一篇文章对容器介绍很全面:laravel 学习笔记 —— 神奇的服务容器
注2: Laravel容器的思想很巧妙,可以仔细阅读上篇文章,结合源码学习,相信有很多收获。

ServiceContainer

其实就是$app, 它是全局的一个容器,是Laravel的核心。
容器(Service Container)实现:vendor/laravel/lumen-framework/src/Application.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Application extends Container
{
use Concerns\RoutesRequests,
Concerns\RegistersExceptionHandlers;
public function withEloquent()
public function withFacades()
public function make($abstract, array $parameters = [])
public function configure($name)
public function register($provider, $options = [], $force = false)
protected function registerCacheBindings()
protected function registerDatabaseBindings()
protected function registerEncrypterBindings()
protected function registerLogBindings()
//////////////////////////////////
// 各种Service Provider的绑定
//////////////////////////////////
}

ServiceProvider

向容器提供不同的服务,通过register函数向容器中注入类实例,从而实现DI(依赖注入),是一种IOC的实现方式。
不同服务的ServiceProvider分布在不同的服务子目录下:

  • cache: vendor/illuminate/cache/CacheServiceProvider.php
  • db: vendor/illuminate/database/DatabaseServiceProvider.php
  • queue: vendor/illuminate/queue/QueueServiceProvider.php

  • 不同的ServiceProvider都继承ServiceProvider;

  • 重写register()方法;
  • $defer变量表征是否延迟加载;
  • provides()方法返回该ServiceProvider可向容器提供的所有服务列表;

总结

本文大致梳理了Laravel/Lumen中Facade, Service Container, Service Provider的不同概念、实现逻辑和调用关系。
Laravel框架的思想:采用容器的方式管理通用功能,实现IOC控制反转,解决依赖注入问题。
其实,只采用Service Provider的方式,在bootstrap/app.php中进行服务注册,在需要的使用调用$app->make(‘db’)的方式也是可行的。
Facades更多的是最大化减少程序依赖,提供一体化的编程体验,这也是Laravel优雅之美。

参考阅读

  1. Response: Don’t Use Facades
  2. laravel 学习笔记 —— 神奇的服务容器
  3. laravel5如何创建service provider和facade