Laravel 框架源码分析---框架启动过程

标签: laravel 源码分析


在我们学习一个框架的过程中,了解一个框架的启动流程,对于我们理解、使用好框架具有很大帮助,今天我们就来看一下 laravel 框架启动过程。

框架启动过程中的相关类

在 laravel 启动过程中,主要涉及到以下类:

  1. Illuminate\Foundation\Application
    Application 是 laravel 框架最核心的类之一。它首先是一个 IOC 容器,管理整个框架类对象的定义、实例化、存储;同时它也是整个框架的应用类,管理应用中的 ServiceProvider,启动、停止应用,启动针对不同应用的启动器。

  2. App\Http\Kernel
    针对 http 请求的核心类,继承自 Illuminate\Foundation\Http\Kernel,管理框架中的路由,配置针对 http 应用的启动器,执行 http 请求,停止针对 http 请求的应用。

  3. Illuminate\Routing\Router
    框架路由系统的门面类,管理并配置路由,将请求分发到路由并返回路由执行请求的响应。

  4. Illuminate\Routing\Route
    框架路由系统的路由类,每个配置的路由对应类的一个实例,里面记录了匹配此路由请求的 uri、methods 等信息,以及对应的执行请求的控制器方法或者回调等信息,实现了判断一个请求是否匹配此路由的方法。

  5. Illuminate\Http\Request
    http 请求类,主要作用是封装 http 请求的query、server、input、 file、cookie、session 等参数。

  6. Illuminate\Http\Response
    http 响应类,构造框架的 http 响应,里面包括响应码、响应内容、响应头等信息。实现了发送响应的方法。

框架启动过程源码分析

接下来,我们来看框架的启动过程。我们知道,针对一个 web 应用,其入口文件是在 /public/index.php,我们来看这个文件具体内容。

//注册应用的自动加载器
require __DIR__.'/../bootstrap/autoload.php';

//创建 Application 类对象,做初始配置并返回
$app = require_once __DIR__.'/../bootstrap/app.php';

//创建针对 http 请求的 $kernel 对象
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

//在 $kernel 中执行请求 $request,并得到响应 $response
//$response 是 Illuminate\Http\Response 类的实例
$response = $kernel->handle(
    //根据 http 请求创建 $request 对象
    //其为 Illuminate\Http\Request 对象实例
    //管理 http 请求的query, server, input, file, cookie, session等信息
    $request = Illuminate\Http\Request::capture()
);

//发送响应
$response->send();

//终止针对请求 $request 得到响应 $response 的 $kernel
$kernel->terminate($request, $response);

我们看了应用入口文件的内容,了解了框架的大概启动流程,接下来我们来分析各个流程的具体内容。

框架的自动加载文件

在在入口文件中,我们看到,代码首先加载了框架的自动加载配置文件,我们来看其具体内容。


define('LARAVEL_START', microtime(true));

//加载由 composer 生成的框架各个包的自动加载规则和我们自定义的自动加载规则
//php 包的自动加载规则主要遵循 psr-4 标准
require __DIR__.'/../vendor/autoload.php';

$compiledPath = __DIR__.'/cache/compiled.php';

if (file_exists($compiledPath)) {
    require $compiledPath;
}

框架 Application 类对象的实例化及配置

当框架加载完自动加载文件后,开始生成核心类 Application 并做基本配置,我们来看具体代码。

//创建 Application 类对象
$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

//添加绑定针对 http 请求的 Kernel 的绑定 
$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
);

return $app;

我们来看 Application 的构造函数,看一下 Application 类对象的实例化过程

namespace Illuminate\Foundation;

class Application extends Container implements ApplicationContract, HttpKernelInterface
{
    /**
     * Create a new Illuminate application instance.
     * 
     * @param  string|null  $basePath
     * @return void
     */
    public function __construct($basePath = null)
    {
        //在 Application 的容器里面添加基础的 binding
        //主要是 app 和 Illuminate\Container\Container
        $this->registerBaseBindings();

        //注册基础的 service provider
        //主要是启动事件监听服务和路由服务的 service provider 
        $this->registerBaseServiceProviders();

        //为框架里面的核心类注册别名
        $this->registerCoreContainerAliases();

        if ($basePath) {
            //设置应用的基础路径,并将基于基础路径得到的常用的其他路径绑定到容器
            $this->setBasePath($basePath);
        }
    }
}

Kernel 对象实例化及基本功能

在入口文件中,我们看到实例化 Application 类对象后,就开始了创建 Kernel 对象,创建请求的 $request 对象,并在 Kernel 对象中执行请求 $request,得到相应 $response。如下代码所示:

//创建针对 http 请求的 $kernel 对象
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

//在 $kernel 中执行请求 $request,并得到响应 $response
//$response 是 Illuminate\Http\Response 类的实例
$response = $kernel->handle(
    //根据 http 请求创建 $request 对象
    //其为 Illuminate\Http\Request 对象实例
    //管理 http 请求的query, server, input, file, cookie, session等信息
    $request = Illuminate\Http\Request::capture()
);

实例化 Kernel 对象

我们先来看 Kernel 对象的实例化过程

namespace Illuminate\Foundation\Http;

use Illuminate\Contracts\Http\Kernel as KernelContract;

class Kernel implements KernelContract{
    /**
     * The application implementation.
     * 
     * @var \Illuminate\Contracts\Foundation\Application
     */
    protected $app;

    /**
     * The router instance.
     *
     * @var \Illuminate\Routing\Router
     */
    protected $router;
    
    /**
     * The application's global HTTP middleware stack.
     * 运行 http 请求全局生效的中间件
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [];
    
    /**
     * The application's route middleware groups.
     * 应用中路由的中间件组
     * @var array
     */
    protected $middlewareGroups = [];

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     * 路由中可能会用到的中间件的简化别名
     * @var array
     */
    protected $routeMiddleware = [];
    
    /**
     * The priority-sorted list of middleware.
     * Forces the listed middleware to always be in the given order.
     * 中间件的优先级,强制下面这些中间件按照给定的顺序执行
     * @var array
     */
    protected $middlewarePriority = [
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \Illuminate\Auth\Middleware\Authenticate::class,
        \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \Illuminate\Auth\Middleware\Authorize::class,
    ];


/**
     * Create a new HTTP kernel instance.
     * 
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @param  \Illuminate\Routing\Router  $router
     * @return void
     */
    public function __construct(Application $app, Router $router)
    {
        $this->app = $app;
        $this->router = $router;
        
        //设置路由的中间件优先级
        $router->middlewarePriority = $this->middlewarePriority;
        
        //设置路由的中间件组
        foreach ($this->middlewareGroups as $key => $middleware) {
            $router->middlewareGroup($key, $middleware);
        }

        //设置路由的中间件别名
        foreach ($this->routeMiddleware as $key => $middleware) {
            $router->middleware($key, $middleware);
        }
    }
}

我们看到在实例化 Kernel 对象过程中,主要设置了路由系统的中间件优先级、中间件别名和中间件组。

Kernel 对象执行请求过程

接下来我们来看在 $kernel 对象中执行请求 $request 并返回相应 $response 的过程。

namespace Illuminate\Foundation\Http;

use Illuminate\Contracts\Http\Kernel as KernelContract;

class Kernel implements KernelContract{
    /**
     * The application implementation.
     * 
     * @var \Illuminate\Contracts\Foundation\Application
     */
    protected $app;

    /**
     * The router instance.
     *
     * @var \Illuminate\Routing\Router
     */
    protected $router;
    
     /**
     * Handle an incoming HTTP request.
     * 执行一个 http 请求
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    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);
        }

        $this->app['events']->fire('kernel.handled', [$request, $response]);

        return $response;
    }
    
     /**
     * Send the given request through the middleware / router.
     * 发送 $request,通过全局中间件,并分发给路由 
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    protected function sendRequestThroughRouter($request)
    {
        //在 Application 类对象上绑定请求类 $request
        $this->app->instance('request', $request);

        Facade::clearResolvedInstance('request');
        
        //根据这个 http 请求启动应用,运行针对 http 请求的启动器
        $this->bootstrap();
        
        //将请求 $request 通过中间件,并分发给路由
        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }
    
     /**
     * Get the route dispatcher callback.
     * 返回在路由上分发请求的闭包
     * @return \Closure
     */
    protected function dispatchToRouter()
    {
        return function ($request) {
            $this->app->instance('request', $request);
            
            //根据路由分发请求
            return $this->router->dispatch($request);
        };
    }
}

我们看到将请求 $response 分发到路由并执行返回其响应调用了路由系统的相关方法,我们来看具体实现。

路由系统分发并执行请求

在 laravel 框架中,Illuminate\Routing\Router 类主要的作用是配置应用的路由,配置路由可能用到的中间件、中间件组,根据配置的路由表分配请求,根据分配到的路由运行请求得到相应。下面我们来看 Illuminate\Routing\Router 类中和框架启动相关的源码

namespace Illuminate\Routing;

class Router implements RegistrarContract
{
  /**
     * Dispatch the request to the application.
     * 分发请求到某个配置的路由
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function dispatch(Request $request)
    {
        $this->currentRequest = $request;

        return $this->dispatchToRoute($request);
    }
    
    /**
     * Dispatch the request to a route and return the response.
     * 分发请求到某个路由,并返回执行请求得到的响应
     * @param  \Illuminate\Http\Request  $request
     * @return mixed
     */
    public function dispatchToRoute(Request $request)
    {
        //首先,在用户配置的路由组里面找到匹配请求的路由
        $route = $this->findRoute($request);

        $request->setRouteResolver(function () use ($route) {
            return $route;
        });

        //触发 RouteMatched 事件
        $this->events->fire(new Events\RouteMatched($route, $request));
        
        //让 $request 通过 $route 配置的中间件,得到运行路由的响应
        $response = $this->runRouteWithinStack($route, $request);

        //根据 $request 和 $response,准备 $response 响应对象
        return $this->prepareResponse($request, $response);
    }
    
       /**
     * 运行路由的中间件,让 $request 通过 $route 配置的中间件
     * @param  \Illuminate\Routing\Route  $route
     * @param  \Illuminate\Http\Request  $request
     * @return mixed
     */
    protected function runRouteWithinStack(Route $route, Request $request)
    {
        //是否应该跳过中间件
        $shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
                                $this->container->make('middleware.disable') === true;

        $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);

        //让请求 $request 通过 $route 的中间件,
        //并最终返回路由执行请求的响应
        return (new Pipeline($this->container))
                        ->send($request)
                        ->through($middleware)
                        ->then(function ($request) use ($route) {
                            return $this->prepareResponse(
                                $request, $route->run($request)
                            );
                        });
    }


  /**
     * Create a response instance from the given value.
     * 根据运行路由得到的响应配置框架的响应
     * @param  \Symfony\Component\HttpFoundation\Request  $request
     * @param  mixed  $response
     * @return \Illuminate\Http\Response
     */
    public function prepareResponse($request, $response)
    {
        if ($response instanceof PsrResponseInterface) {
            $response = (new HttpFoundationFactory)->createResponse($response);
        } elseif (! $response instanceof SymfonyResponse) {
            $response = new Response($response);
        }

        return $response->prepare($request);
    }
}

laravel 框架路由系统中,类 Illuminate\Routing\Route 也是重要的核心类之一,在框架中,每配置一个请求的路由,都会生成一个 Illuminate\Routing\Route 类的实例,里面记录类请求的 uri、methods,以及对应的执行请求的控制器方法或者回调等信息。接下来我们来看类 Illuminate\Routing\Route 中和框架启动相关的内容。

namespace Illuminate\Routing;

class Route
{
 
   /**
     * Run the route action and return the response.
     * 运行路由配置的 action 并返回其响应
     * @param  \Illuminate\Http\Request  $request
     * @return mixed
     */
    public function run(Request $request)
    {
        $this->container = $this->container ?: new Container;

        try {
            //如果路由是有控制器的方法执行
            //运行控制器对应的方法并返回
            if ($this->isControllerAction()) {
                return $this->runController();
            }
            
            //运行并返回路由对应的回调
            return $this->runCallable();
        } catch (HttpResponseException $e) {
            return $e->getResponse();
        }
    }
}

http应用的启动

在 Kernel 对象执行请求过程中,我们看到在将请求分发到路由之前,我们先启动了针对这个 http 请求的应用(通过语句 $this->bootstrap() 实现,$this 表示对象 $kernel )。接下来我们来看应用的启动过程。

namespace Illuminate\Foundation\Http;

use Illuminate\Contracts\Http\Kernel as KernelContract;

class Kernel implements KernelContract{
    /**
     * The application implementation.
     * 
     * @var \Illuminate\Contracts\Foundation\Application
     */
    protected $app;

    /**
     * The bootstrap classes for the application.
     *  启动一个 http 请求的应用所需的启动器的类
     * @var array
     */
    protected $bootstrappers = [
        'Illuminate\Foundation\Bootstrap\DetectEnvironment',
        'Illuminate\Foundation\Bootstrap\LoadConfiguration',
        'Illuminate\Foundation\Bootstrap\ConfigureLogging',
        'Illuminate\Foundation\Bootstrap\HandleExceptions',
        'Illuminate\Foundation\Bootstrap\RegisterFacades',
        'Illuminate\Foundation\Bootstrap\RegisterProviders',
        'Illuminate\Foundation\Bootstrap\BootProviders',
    ];
    
     /**
     * Bootstrap the application for HTTP requests.
     * 根据这个 http 请求启动应用,运行针对 http 请求的启动器
     * @return void
     */
    public function bootstrap()
    {
        if (! $this->app->hasBeenBootstrapped()) {
            $this->app->bootstrapWith($this->bootstrappers());
        }
    }
    
    /**
     * Get the bootstrap classes for the application.
     * 返回针对 http 请求的启动器类 
     * @return array
     */
    protected function bootstrappers()
    {
        return $this->bootstrappers;
    }
}

我看再来看 Application 类中启动一组启动器的相关代码

namespace Illuminate\Foundation;
class Application extends Container implements ApplicationContract, HttpKernelInterface
{

    /**
     * Run the given array of bootstrap classes.
     * 启动 $bootstrappers 数组里面的启动器
     * @param  array  $bootstrappers
     * @return void
     */
    public function bootstrapWith(array $bootstrappers)
    {
        $this->hasBeenBootstrapped = true;
        foreach ($bootstrappers as $bootstrapper) {
            //触发启动器 $bootstrapper 启动的监听事件
            $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
            $this->make($bootstrapper)->bootstrap($this);
            //触发启动器 $bootstrapper 启动后的监听事件
            $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
        }
    }
}

根据以上代码,我们知道所谓启动针对 http 请求的应用,也就是依次运行在 $kernel 对象中配置的启动器,每个启动器实现了启动应用过程中的某一块功能。

各个启动器具体功能如下:

  1. Illuminate\Foundation\Bootstrap\DetectEnvironment
    检测并配置环境。当框架没有缓存配置的时候,加载 .env 文件里面的配置信息

  2. Illuminate\Foundation\Bootstrap\LoadConfiguration
    加载框架的配置信息。当配置文件已经缓存,从配置缓存文件中加载配置信息,否则依次加载框架的配置文件里面的配置信息

  3. Illuminate\Foundation\Bootstrap\ConfigureLogging
    注册、配置并启动框架的日志系统

  4. Illuminate\Foundation\Bootstrap\HandleExceptions
    设置框架运行时的异常报错机制

  5. Illuminate\Foundation\Bootstrap\RegisterFacades
    注册框架的门面

  6. Illuminate\Foundation\Bootstrap\RegisterProviders
    注册框架的 service provider,也就是运行非延迟执行的 ServiceProvider 的 register 方法(执行 $app->registerConfiguredProviders() 语句实现)

  7. Illuminate\Foundation\Bootstrap\BootProviders
    启动框架的 service provider,也就是运行非延迟执行的 ServiceProvider 的 boot 方法(通过执行 $app->boot() 语句实现)

由于框架是依次执行各个启动器,所以我们知道了为什么 laravel 框架中, ServiceProvider 先执行 register 方法,后执行 boot 方法。

终止应用相关源码

在将请求分发到路由并执行得到相应之后,开始发送响应并在 Kernel 类对象中终止应用:

//发送响应内容
$response->send();
//终止针对请求 $request 得到响应 $response 的 $kernel
$kernel->terminate($request, $response);

我们看到终止应用是通过 $kernel 对象调用 terminate 方法得到的,我们来看相应源码

namespace Illuminate\Foundation\Http;

use Illuminate\Contracts\Http\Kernel as KernelContract;

class Kernel implements KernelContract{
    /**
     * Call the terminate method on any terminable middleware.
     * 停止这个针对 http 请求的应用
     * @param  \Illuminate\Http\Request  $request
     * @param  \Illuminate\Http\Response  $response
     * @return void
     */
    public function terminate($request, $response)
    {
        //获取请求经过的中间件
        $middlewares = $this->app->shouldSkipMiddleware() ? [] : array_merge(
            $this->gatherRouteMiddleware($request),
            $this->middleware
        );
        //运行各个中间件的 terminate 方法
        foreach ($middlewares as $middleware) {
            if (! is_string($middleware)) {
                continue;
            }
            list($name, $parameters) = $this->parseMiddleware($middleware);
            $instance = $this->app->make($name);
            if (method_exists($instance, 'terminate')) {
                $instance->terminate($request, $response);
            }
        }
        //终止应用,应用 Application 对象的 terminate 方法
        $this->app->terminate();
    }
}

$kernel 终止的过程中,调用了 Application 类对象的 terminate 方法,我们来看一下其相关方法

namespace Illuminate\Foundation;
class Application extends Container implements ApplicationContract, HttpKernelInterface
{
/**
     * Boot the application's service providers.
     * 启动这个 application 的 service provider
     * @return void
     */
    public function boot()
    {
        //如果应用已经启动,则直接返回
        if ($this->booted) {
            return;
        }
        //触发应用的启动的回调函数
        $this->fireAppCallbacks($this->bootingCallbacks);
        //启动框架中的设置的 service provider
        //array_walk 函数对数组中的每个元素应用用户自定义函数
        //在函数中,第一个参数是值,第二个参数是键。
        array_walk($this->serviceProviders, function ($p) {
            $this->bootProvider($p);
        });
        //设置应用已经启动
        $this->booted = true;
        //触发应用启动后的回调函数
        $this->fireAppCallbacks($this->bootedCallbacks);
    }
    /**
     * Terminate the application.
     * 终止这个应用
     * @return void
     */
    public function terminate()
    {
        //运行应用结束时的回调
        foreach ($this->terminatingCallbacks as $terminating) {
            $this->call($terminating);
        }
    }
}

通过以上的代码分析,我们可以看到 Application 类对象的 boot 方法是通过 Kernel 对象配置的启动器 Illuminate\Foundation\Bootstrap\BootProviders 来调用的,而 terminate 方法则是通过 Kernel 类对象的 terminate 方法来调用的。

总结

至此,我们大概了解了 laravel 框架的启动过程。了解框架的启动执行过程对于我们调试代码、理解框架设计思想具有很大帮助。
不过,框架的设计是一个整体性的东西,我们不仅要了解框架的启动过程,还要知道其他模块的运行原理,比如 Application 对 ServiceProvider 的管理,框架中间件的实现过程,Facade 的设计思想及实现,这些都是框架里面的核心概念和内容,理解这些东西,对于我们更好的理解和使用框架具有很大的帮助。大家可以看我相关的文章,通过这些文章的阅读,相信对理解框架启动过程和整体设计思想具有很大帮助。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356

推荐阅读更多精彩内容

  • 先说几句废话,调和气氛。事情的起由来自客户需求频繁变更,伟大的师傅决定横刀立马的改革使用新的框架(created ...
    wsdadan阅读 3,054评论 0 12
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,673评论 18 139
  • Laravel 学习交流 QQ 群:375462817 本文档前言Laravel 文档写的很好,只是新手看起来会有...
    Leonzai阅读 7,924评论 2 12
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架,建立于...
    Hsinwong阅读 22,423评论 1 92
  • 很久以前就看过一篇文章,主人公是一个由狼群养大的小女孩,这个小女孩不能人言却可以狼嚎,夜里的眼睛还可以发出绿色的光...
    默默喜欢你阅读 517评论 1 2