初學(xué)Laravel難免對(duì)項(xiàng)目組織結(jié)構(gòu)疮胖、工作流程不太了解环戈,本文以盡量簡(jiǎn)單的方式向大家做大概介紹。
組織結(jié)構(gòu)說明
成功創(chuàng)建Laravel后澎灸,項(xiàng)目組織結(jié)構(gòu)如下圖所示院塞。
運(yùn)行效果
合理配置web服務(wù)器后運(yùn)行,正常運(yùn)行效果如下性昭。
啟動(dòng)流程分析
Laravel是如何展示上面網(wǎng)頁的呢拦止,下面我們來慢慢揭曉。
下圖為我簡(jiǎn)單整理的啟動(dòng)流程序列圖糜颠,估計(jì)不太清晰汹族,建議大家下載原圖查看。
備注:為了圖片簡(jiǎn)單其兴,上面序列圖中鞠抑,大部分函數(shù)調(diào)用的返回箭頭我是省略掉了,相信略有基礎(chǔ)的人應(yīng)該都可以看懂忌警。
一搁拙、創(chuàng)建App對(duì)象
在Laravel中一個(gè)app對(duì)象就是一個(gè)Container(服務(wù)容器,概念不清晰的推薦閱讀科普好文及官方介紹),這就意味著app可以綁定各類Provider(服務(wù)提供者)法绵。
public/index.php是程序入口箕速,public/index.php中通過如下代碼創(chuàng)建一個(gè)app對(duì)象。
$app = require_once __DIR__.'/../bootstrap/app.php';
進(jìn)入bootstrap/app.php文件朋譬,我們看到如下代碼盐茎,該代碼創(chuàng)建了一個(gè)Application對(duì)象實(shí)例。
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
進(jìn)入Application源文件徙赢,我們可以看到這樣的類定義:
class Application extends Container implements ApplicationContract, HttpKernelInterface
可以看出來Application就是一個(gè)Container字柠,同時(shí)Application也繼承了ApplicationContract及HttpKernelInterface接口探越。
再深入一點(diǎn),我們可以看到ApplicationContract主要定義了一組獲取項(xiàng)目路徑窑业、環(huán)境信息钦幔、Provider(服務(wù)提供者)的接口,及程序啟動(dòng)相關(guān)的boot()常柄、booted()等接口鲤氢。而HttpKernelInterface主要定義了http相關(guān)的事件處理接口handle()。
二西潘、綁定服務(wù)
我們繼續(xù)閱讀bootstrap/app.php文件卷玉,可以看到如下代碼
//注冊(cè)一個(gè)App\Http\Kernel服務(wù)Class,這個(gè)類主要用于http相關(guān)事件處理
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
//注冊(cè)一個(gè)App\Console\Kernel服務(wù)Class喷市,這里我還不太清楚該類的作用
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
//注冊(cè)異常處理Handler相种,沒深入去研究,看代碼基本上是接受到異常后還是交給父類處理
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
上面代碼中很關(guān)鍵的一個(gè)點(diǎn)是注冊(cè)App\Http\Kernel品姓,從上面流程圖你可以大致感覺到這個(gè)類的重要性蚂子。
如果你困惑為什么注冊(cè)的函數(shù)名叫singleton,建議你看看函數(shù)內(nèi)部實(shí)現(xiàn)缭黔,其實(shí)內(nèi)部就是調(diào)用的Container->bind()方法食茎,只是對(duì)外換了個(gè)名字而已。
通常馏谨,綁定并不會(huì)實(shí)例化對(duì)象别渔,綁定可以簡(jiǎn)單理解為通過一個(gè)字典將interface跟具體的class(或匿名函數(shù)Closure)關(guān)聯(lián)起來,其中字典的key就是interface的名字惧互,字典的value就是class名字哎媚,理解這一點(diǎn)非常重要,因?yàn)檫@是Container實(shí)現(xiàn)di(dependency injection依賴注入)和ioc(Inversion Of Control控制反轉(zhuǎn))很重要的一環(huán)喊儡。
三垛玻、實(shí)例化各類服務(wù)及路由處理
回到public/index.php范删,我們看到如下代碼衷快。
//實(shí)例化一個(gè)kernel對(duì)象业筏。
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
上面代碼中app->make才是真正將App\Http\Kernel對(duì)象實(shí)例化。怎么實(shí)例化的呢匆赃?前面我們有講到淤毛,綁定就是將interface名跟與之對(duì)應(yīng)的class存入一個(gè)字典中,而make就是通過interface名找到對(duì)應(yīng)的class名算柳,并用反射機(jī)制創(chuàng)建對(duì)應(yīng)的class實(shí)例低淡。
kernel->handle()傳入一個(gè)request并返回一個(gè)response對(duì)象。別看這里只有一行代碼,實(shí)際上做的事情非常之多蔗蹋,該函數(shù)內(nèi)部調(diào)用的核心代碼如下:
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());
}
其中bootstrap()接口會(huì)調(diào)用app相關(guān)方法來實(shí)例化LoadConfiguration何荚、BootProviders、RegisterProviders等對(duì)象猪杭。
app對(duì)象通過讀取config/app.php配置文件來設(shè)置App\Providers\RouteServiceProvider餐塘、
App\Providers\AppServiceProvider等。
'providers' => [
//是的胁孙,為了簡(jiǎn)潔唠倦,這里我刪除了一些系統(tǒng)默認(rèn)provider称鳞,大家可以去查閱源碼
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
],
獲取完配置信息之后app會(huì)初始化一個(gè)App\Providers\RouteServiceProvider對(duì)象涮较,并調(diào)用其boot()方法,boot()最終會(huì)設(shè)置好程序的路由映射關(guān)系。
RouteServiceProvider是很重要的一個(gè)類冈止,我們看下路由映射關(guān)系代碼狂票。
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*
* @return void
*/
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
通過代碼我們發(fā)現(xiàn),web接口對(duì)應(yīng)的路由配置文件是routes/web.php熙暴。
打開routes/web.php闺属,我們可以看到如下代碼。
Route::get('/', function () {
return view('welcome');
});
從代碼我們大致能夠猜測(cè)周霉,當(dāng)用戶訪問網(wǎng)站首頁的時(shí)候?qū)⒎祷匾粋€(gè)'welcome'頁面掂器,而這個(gè)頁面實(shí)際上就是resources/views/welcome.blade.php。我們可以debug一下view()俱箱,其內(nèi)部有一組規(guī)則將'welcome'映射到welcome.blade.php文件国瓮。
設(shè)置好路由之后kernel對(duì)象會(huì)調(diào)用dispatchToRouter(),并最終生成一個(gè)response對(duì)象狞谱,response對(duì)象里面包含welcome.blade.php返回的網(wǎng)頁數(shù)據(jù)乃摹。
輸出內(nèi)容
在回到public/index.php,下面的事情就變得簡(jiǎn)單跟衅。
//將頁面內(nèi)容輸出到調(diào)用者(這里指瀏覽器)
$response->send();
//掃尾工作
$kernel->terminate($request, $response);
以上是Laravel工作流程的分析孵睬,如果感覺還是不太理解,建議認(rèn)真閱讀Laravel中文手冊(cè)伶跷。
同時(shí)也推薦大家看看如下推薦文章掰读,不同的作者有不同的介紹方式,個(gè)人感悟也可能不一樣叭莫。
http://www.reibang.com/p/509a8dd5654e
http://www.reibang.com/p/63a3d76e7aca