Laravel 框架源碼分析---框架啟動過程

標(biāo)簽: laravel 源碼分析


在我們學(xué)習(xí)一個框架的過程中,了解一個框架的啟動流程,對于我們理解、使用好框架具有很大幫助,今天我們就來看一下 laravel 框架啟動過程。

框架啟動過程中的相關(guān)類

在 laravel 啟動過程中,主要涉及到以下類:

  1. Illuminate\Foundation\Application
    Application 是 laravel 框架最核心的類之一。它首先是一個 IOC 容器,管理整個框架類對象的定義、實例化、存儲;同時它也是整個框架的應(yīng)用類,管理應(yīng)用中的 ServiceProvider,啟動、停止應(yīng)用,啟動針對不同應(yīng)用的啟動器。

  2. App\Http\Kernel
    針對 http 請求的核心類,繼承自 Illuminate\Foundation\Http\Kernel,管理框架中的路由,配置針對 http 應(yīng)用的啟動器,執(zhí)行 http 請求,停止針對 http 請求的應(yīng)用。

  3. Illuminate\Routing\Router
    框架路由系統(tǒng)的門面類,管理并配置路由,將請求分發(fā)到路由并返回路由執(zhí)行請求的響應(yīng)。

  4. Illuminate\Routing\Route
    框架路由系統(tǒng)的路由類,每個配置的路由對應(yīng)類的一個實例,里面記錄了匹配此路由請求的 uri、methods 等信息,以及對應(yīng)的執(zhí)行請求的控制器方法或者回調(diào)等信息,實現(xiàn)了判斷一個請求是否匹配此路由的方法。

  5. Illuminate\Http\Request
    http 請求類,主要作用是封裝 http 請求的query、server、input、 file、cookie、session 等參數(shù)。

  6. Illuminate\Http\Response
    http 響應(yīng)類,構(gòu)造框架的 http 響應(yīng),里面包括響應(yīng)碼、響應(yīng)內(nèi)容、響應(yīng)頭等信息。實現(xiàn)了發(fā)送響應(yīng)的方法。

框架啟動過程源碼分析

接下來,我們來看框架的啟動過程。我們知道,針對一個 web 應(yīng)用,其入口文件是在 /public/index.php,我們來看這個文件具體內(nèi)容。

//注冊應(yīng)用的自動加載器
require __DIR__.'/../bootstrap/autoload.php';

//創(chuàng)建 Application 類對象,做初始配置并返回
$app = require_once __DIR__.'/../bootstrap/app.php';

//創(chuàng)建針對 http 請求的 $kernel 對象
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

//在 $kernel 中執(zhí)行請求 $request,并得到響應(yīng) $response
//$response 是 Illuminate\Http\Response 類的實例
$response = $kernel->handle(
    //根據(jù) http 請求創(chuàng)建 $request 對象
    //其為 Illuminate\Http\Request 對象實例
    //管理 http 請求的query, server, input, file, cookie, session等信息
    $request = Illuminate\Http\Request::capture()
);

//發(fā)送響應(yīng)
$response->send();

//終止針對請求 $request 得到響應(yīng) $response 的 $kernel
$kernel->terminate($request, $response);

我們看了應(yīng)用入口文件的內(nèi)容,了解了框架的大概啟動流程,接下來我們來分析各個流程的具體內(nèi)容。

框架的自動加載文件

在在入口文件中,我們看到,代碼首先加載了框架的自動加載配置文件,我們來看其具體內(nèi)容。


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

//加載由 composer 生成的框架各個包的自動加載規(guī)則和我們自定義的自動加載規(guī)則
//php 包的自動加載規(guī)則主要遵循 psr-4 標(biāo)準
require __DIR__.'/../vendor/autoload.php';

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

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

框架 Application 類對象的實例化及配置

當(dāng)框架加載完自動加載文件后,開始生成核心類 Application 并做基本配置,我們來看具體代碼。

//創(chuàng)建 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 的構(gòu)造函數(shù),看一下 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 的容器里面添加基礎(chǔ)的 binding
        //主要是 app 和 Illuminate\Container\Container
        $this->registerBaseBindings();

        //注冊基礎(chǔ)的 service provider
        //主要是啟動事件監(jiān)聽服務(wù)和路由服務(wù)的 service provider 
        $this->registerBaseServiceProviders();

        //為框架里面的核心類注冊別名
        $this->registerCoreContainerAliases();

        if ($basePath) {
            //設(shè)置應(yīng)用的基礎(chǔ)路徑,并將基于基礎(chǔ)路徑得到的常用的其他路徑綁定到容器
            $this->setBasePath($basePath);
        }
    }
}

Kernel 對象實例化及基本功能

在入口文件中,我們看到實例化 Application 類對象后,就開始了創(chuàng)建 Kernel 對象,創(chuàng)建請求的 $request 對象,并在 Kernel 對象中執(zhí)行請求 $request,得到相應(yīng) $response。如下代碼所示:

//創(chuàng)建針對 http 請求的 $kernel 對象
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

//在 $kernel 中執(zhí)行請求 $request,并得到響應(yīng) $response
//$response 是 Illuminate\Http\Response 類的實例
$response = $kernel->handle(
    //根據(jù) http 請求創(chuàng)建 $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.
     * 應(yīng)用中路由的中間件組
     * @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.
     * 中間件的優(yōu)先級,強制下面這些中間件按照給定的順序執(zhí)行
     * @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;
        
        //設(shè)置路由的中間件優(yōu)先級
        $router->middlewarePriority = $this->middlewarePriority;
        
        //設(shè)置路由的中間件組
        foreach ($this->middlewareGroups as $key => $middleware) {
            $router->middlewareGroup($key, $middleware);
        }

        //設(shè)置路由的中間件別名
        foreach ($this->routeMiddleware as $key => $middleware) {
            $router->middleware($key, $middleware);
        }
    }
}

我們看到在實例化 Kernel 對象過程中,主要設(shè)置了路由系統(tǒng)的中間件優(yōu)先級、中間件別名和中間件組。

Kernel 對象執(zhí)行請求過程

接下來我們來看在 $kernel 對象中執(zhí)行請求 $request 并返回相應(yīng) $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.
     * 執(zhí)行一個 http 請求
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function handle($request)
    {
        try {
            $request->enableHttpMethodParameterOverride();
                
            //將請求通過中間件分發(fā)給路由    
            $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.
     * 發(fā)送 $request,通過全局中間件,并分發(fā)給路由 
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    protected function sendRequestThroughRouter($request)
    {
        //在 Application 類對象上綁定請求類 $request
        $this->app->instance('request', $request);

        Facade::clearResolvedInstance('request');
        
        //根據(jù)這個 http 請求啟動應(yīng)用,運行針對 http 請求的啟動器
        $this->bootstrap();
        
        //將請求 $request 通過中間件,并分發(fā)給路由
        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }
    
     /**
     * Get the route dispatcher callback.
     * 返回在路由上分發(fā)請求的閉包
     * @return \Closure
     */
    protected function dispatchToRouter()
    {
        return function ($request) {
            $this->app->instance('request', $request);
            
            //根據(jù)路由分發(fā)請求
            return $this->router->dispatch($request);
        };
    }
}

我們看到將請求 $response 分發(fā)到路由并執(zhí)行返回其響應(yīng)調(diào)用了路由系統(tǒng)的相關(guān)方法,我們來看具體實現(xiàn)。

路由系統(tǒng)分發(fā)并執(zhí)行請求

在 laravel 框架中,Illuminate\Routing\Router 類主要的作用是配置應(yīng)用的路由,配置路由可能用到的中間件、中間件組,根據(jù)配置的路由表分配請求,根據(jù)分配到的路由運行請求得到相應(yīng)。下面我們來看 Illuminate\Routing\Router 類中和框架啟動相關(guān)的源碼

namespace Illuminate\Routing;

class Router implements RegistrarContract
{
  /**
     * Dispatch the request to the application.
     * 分發(fā)請求到某個配置的路由
     * @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.
     * 分發(fā)請求到某個路由,并返回執(zhí)行請求得到的響應(yīng)
     * @param  \Illuminate\Http\Request  $request
     * @return mixed
     */
    public function dispatchToRoute(Request $request)
    {
        //首先,在用戶配置的路由組里面找到匹配請求的路由
        $route = $this->findRoute($request);

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

        //觸發(fā) RouteMatched 事件
        $this->events->fire(new Events\RouteMatched($route, $request));
        
        //讓 $request 通過 $route 配置的中間件,得到運行路由的響應(yīng)
        $response = $this->runRouteWithinStack($route, $request);

        //根據(jù) $request 和 $response,準備 $response 響應(yīng)對象
        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)
    {
        //是否應(yīng)該跳過中間件
        $shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
                                $this->container->make('middleware.disable') === true;

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

        //讓請求 $request 通過 $route 的中間件,
        //并最終返回路由執(zhí)行請求的響應(yīng)
        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.
     * 根據(jù)運行路由得到的響應(yīng)配置框架的響應(yīng)
     * @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 框架路由系統(tǒng)中,類 Illuminate\Routing\Route 也是重要的核心類之一,在框架中,每配置一個請求的路由,都會生成一個 Illuminate\Routing\Route 類的實例,里面記錄類請求的 uri、methods,以及對應(yīng)的執(zhí)行請求的控制器方法或者回調(diào)等信息。接下來我們來看類 Illuminate\Routing\Route 中和框架啟動相關(guān)的內(nèi)容。

namespace Illuminate\Routing;

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

        try {
            //如果路由是有控制器的方法執(zhí)行
            //運行控制器對應(yīng)的方法并返回
            if ($this->isControllerAction()) {
                return $this->runController();
            }
            
            //運行并返回路由對應(yīng)的回調(diào)
            return $this->runCallable();
        } catch (HttpResponseException $e) {
            return $e->getResponse();
        }
    }
}

http應(yīng)用的啟動

在 Kernel 對象執(zhí)行請求過程中,我們看到在將請求分發(fā)到路由之前,我們先啟動了針對這個 http 請求的應(yīng)用(通過語句 $this->bootstrap() 實現(xiàn),$this 表示對象 $kernel )。接下來我們來看應(yīng)用的啟動過程。

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 請求的應(yīng)用所需的啟動器的類
     * @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.
     * 根據(jù)這個 http 請求啟動應(yīng)用,運行針對 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 類中啟動一組啟動器的相關(guān)代碼

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

    /**
     * Run the given array of bootstrap classes.
     * 啟動 $bootstrappers 數(shù)組里面的啟動器
     * @param  array  $bootstrappers
     * @return void
     */
    public function bootstrapWith(array $bootstrappers)
    {
        $this->hasBeenBootstrapped = true;
        foreach ($bootstrappers as $bootstrapper) {
            //觸發(fā)啟動器 $bootstrapper 啟動的監(jiān)聽事件
            $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
            $this->make($bootstrapper)->bootstrap($this);
            //觸發(fā)啟動器 $bootstrapper 啟動后的監(jiān)聽事件
            $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
        }
    }
}

根據(jù)以上代碼,我們知道所謂啟動針對 http 請求的應(yīng)用,也就是依次運行在 $kernel 對象中配置的啟動器,每個啟動器實現(xiàn)了啟動應(yīng)用過程中的某一塊功能。

各個啟動器具體功能如下:

  1. Illuminate\Foundation\Bootstrap\DetectEnvironment
    檢測并配置環(huán)境。當(dāng)框架沒有緩存配置的時候,加載 .env 文件里面的配置信息

  2. Illuminate\Foundation\Bootstrap\LoadConfiguration
    加載框架的配置信息。當(dāng)配置文件已經(jīng)緩存,從配置緩存文件中加載配置信息,否則依次加載框架的配置文件里面的配置信息

  3. Illuminate\Foundation\Bootstrap\ConfigureLogging
    注冊、配置并啟動框架的日志系統(tǒng)

  4. Illuminate\Foundation\Bootstrap\HandleExceptions
    設(shè)置框架運行時的異常報錯機制

  5. Illuminate\Foundation\Bootstrap\RegisterFacades
    注冊框架的門面

  6. Illuminate\Foundation\Bootstrap\RegisterProviders
    注冊框架的 service provider,也就是運行非延遲執(zhí)行的 ServiceProvider 的 register 方法(執(zhí)行 $app->registerConfiguredProviders() 語句實現(xiàn))

  7. Illuminate\Foundation\Bootstrap\BootProviders
    啟動框架的 service provider,也就是運行非延遲執(zhí)行的 ServiceProvider 的 boot 方法(通過執(zhí)行 $app->boot() 語句實現(xiàn))

由于框架是依次執(zhí)行各個啟動器,所以我們知道了為什么 laravel 框架中, ServiceProvider 先執(zhí)行 register 方法,后執(zhí)行 boot 方法。

終止應(yīng)用相關(guān)源碼

在將請求分發(fā)到路由并執(zhí)行得到相應(yīng)之后,開始發(fā)送響應(yīng)并在 Kernel 類對象中終止應(yīng)用:

//發(fā)送響應(yīng)內(nèi)容
$response->send();
//終止針對請求 $request 得到響應(yīng) $response 的 $kernel
$kernel->terminate($request, $response);

我們看到終止應(yīng)用是通過 $kernel 對象調(diào)用 terminate 方法得到的,我們來看相應(yīng)源碼

namespace Illuminate\Foundation\Http;

use Illuminate\Contracts\Http\Kernel as KernelContract;

class Kernel implements KernelContract{
    /**
     * Call the terminate method on any terminable middleware.
     * 停止這個針對 http 請求的應(yīng)用
     * @param  \Illuminate\Http\Request  $request
     * @param  \Illuminate\Http\Response  $response
     * @return void
     */
    public function terminate($request, $response)
    {
        //獲取請求經(jīng)過的中間件
        $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);
            }
        }
        //終止應(yīng)用,應(yīng)用 Application 對象的 terminate 方法
        $this->app->terminate();
    }
}

$kernel 終止的過程中,調(diào)用了 Application 類對象的 terminate 方法,我們來看一下其相關(guān)方法

namespace Illuminate\Foundation;
class Application extends Container implements ApplicationContract, HttpKernelInterface
{
/**
     * Boot the application's service providers.
     * 啟動這個 application 的 service provider
     * @return void
     */
    public function boot()
    {
        //如果應(yīng)用已經(jīng)啟動,則直接返回
        if ($this->booted) {
            return;
        }
        //觸發(fā)應(yīng)用的啟動的回調(diào)函數(shù)
        $this->fireAppCallbacks($this->bootingCallbacks);
        //啟動框架中的設(shè)置的 service provider
        //array_walk 函數(shù)對數(shù)組中的每個元素應(yīng)用用戶自定義函數(shù)
        //在函數(shù)中,第一個參數(shù)是值,第二個參數(shù)是鍵。
        array_walk($this->serviceProviders, function ($p) {
            $this->bootProvider($p);
        });
        //設(shè)置應(yīng)用已經(jīng)啟動
        $this->booted = true;
        //觸發(fā)應(yīng)用啟動后的回調(diào)函數(shù)
        $this->fireAppCallbacks($this->bootedCallbacks);
    }
    /**
     * Terminate the application.
     * 終止這個應(yīng)用
     * @return void
     */
    public function terminate()
    {
        //運行應(yīng)用結(jié)束時的回調(diào)
        foreach ($this->terminatingCallbacks as $terminating) {
            $this->call($terminating);
        }
    }
}

通過以上的代碼分析,我們可以看到 Application 類對象的 boot 方法是通過 Kernel 對象配置的啟動器 Illuminate\Foundation\Bootstrap\BootProviders 來調(diào)用的,而 terminate 方法則是通過 Kernel 類對象的 terminate 方法來調(diào)用的。

總結(jié)

至此,我們大概了解了 laravel 框架的啟動過程。了解框架的啟動執(zhí)行過程對于我們調(diào)試代碼、理解框架設(shè)計思想具有很大幫助。
不過,框架的設(shè)計是一個整體性的東西,我們不僅要了解框架的啟動過程,還要知道其他模塊的運行原理,比如 Application 對 ServiceProvider 的管理,框架中間件的實現(xiàn)過程,F(xiàn)acade 的設(shè)計思想及實現(xiàn),這些都是框架里面的核心概念和內(nèi)容,理解這些東西,對于我們更好的理解和使用框架具有很大的幫助。大家可以看我相關(guān)的文章,通過這些文章的閱讀,相信對理解框架啟動過程和整體設(shè)計思想具有很大幫助。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 先說幾句廢話,調(diào)和氣氛。事情的起由來自客戶需求頻繁變更,偉大的師傅決定橫刀立馬的改革使用新的框架(created ...
    wsdadan閱讀 3,193評論 0 12
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評論 19 139
  • Laravel 學(xué)習(xí)交流 QQ 群:375462817 本文檔前言Laravel 文檔寫的很好,只是新手看起來會有...
    Leonzai閱讀 8,708評論 2 12
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架,建立于...
    Hsinwong閱讀 22,940評論 1 92
  • 很久以前就看過一篇文章,主人公是一個由狼群養(yǎng)大的小女孩,這個小女孩不能人言卻可以狼嚎,夜里的眼睛還可以發(fā)出綠色的光...
    默默喜歡你閱讀 600評論 1 2

友情鏈接更多精彩內(nèi)容