也許beego框架在國內應該是衆多PHPer轉go的首選,由於beego的MVC、ORM、完善的中文文檔讓PHPer們駕輕就熟,毫無疑問我也是。這種感受就像當年入門PHP時使用ThinkPHP同樣。git
也許隨着你的認知的提高,你會討厭如今東西,好比某一天你可能慢慢的開始討厭beego,你會發現go語言裏包的真正意義,你開始反思MVC真的適合go嗎,或者你開始覺着ORM在靜態語言裏的雞肋,等等。我只想說:「也許你成長了~」。可是這些都不重要,每個受歡迎的事物天然有咱們值的學習的地方。今天這篇文章很簡單,像一篇筆記,記錄了我這幾天抽空讀beego源碼的記錄。github
如何讀一個框架?
毫無疑問讀go的框架和PHP框架也是同樣的:sql
這裏(1.)和(3.)無非就是加載個文件和sql解析器的實現,我就忽略了,重點就看看路由的實現。app
簡單帶過:框架
// Step1: 安裝beego go get github.com/astaxie/beego // Step2: 安裝bee go get github.com/beego/bee // Step3: 用bee工具建立一個新的項目 bee new beego-code-read
go有本身實現的http包,大多go框架也是基於這個http包,因此看beego以前咱們先補充或者複習下這個知識點。以下:curl
package main import ( // 導入net/http包 "net/http" ) func main() { // ------------------ 使用http包啓動一個http服務 方式一 ------------------ // *http.Request http請求內容實例的指針 // http.ResponseWriter 寫http響應內容的實例 http.HandleFunc("/v1/demo", func(w http.ResponseWriter, r *http.Request) { // 寫入響應內容 w.Write([]byte("Hello TIGERB !\n")) }) // 啓動一個http服務並監聽8888端口 這裏第二個參數能夠指定handler http.ListenAndServe(":8888", nil) } // 測試咱們的服務 // -------------------- // 啓動:bee run // 訪問: curl "http://127.0.0.1:8888/v1/demo" // 響應結果:Hello TIGERB !
ListenAndServe是對http.Server的進一步封裝,除了上面的方式,還能夠使用http.Server直接啓服務,這個須要設置Handler,這個Handler要實現Server.Handler這個接口。當請求來了會執行這個Handler的ServeHTTP
方法,以下:tcp
package main // 導入net/http包 import ( "net/http" ) // DemoHandle server handle示例 type DemoHandle struct { } // ServeHTTP 匹配到路由後執行的方法 func (DemoHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello TIGERB !\n")) } func main() { // ------------------ 使用http包的Server啓動一個http服務 方式二 ------------------ // 初始化一個http.Server server := &http.Server{} // 初始化handler並賦值給server.Handler server.Handler = DemoHandle{} // 綁定地址 server.Addr = ":8888" // 啓動一個http服務 server.ListenAndServe() } // 測試咱們的服務 // -------------------- // 啓動:bee run // 訪問: curl "http://127.0.0.1:8888/v1/demo" // 響應結果:Hello TIGERB !
接下里咱們開始看beego的代碼。拿訪問"http://127.0.0.1:8080/"
來講,對於beego代碼來講有三個關鍵點,分別以下:工具
beego.Run()
beego.Router("/", &controllers.MainController{})
Get()
下面來看3個關鍵點的詳細分析:學習
// github.com/astaxie/beego/beego.go func Run(params ...string) { // 啓動http服務以前的一些初始化 忽略 往下看 initBeforeHTTPRun() // http服務的ip&port設置 if len(params) > 0 && params[0] != "" { strs := strings.Split(params[0], ":") if len(strs) > 0 && strs[0] != "" { BConfig.Listen.HTTPAddr = strs[0] } if len(strs) > 1 && strs[1] != "" { BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1]) } } // 又一個run 往下看 BeeApp.Run() }
// github.com/astaxie/beego/app.go func (app *App) Run(mws ...MiddleWare) { // ... 省略 // 看了下這裏app.Server的類型就是*http.Server 也就是說用的原生http包 且是上面「go如何啓動一個http server」中的第二種方式 app.Server.Handler = app.Handlers // ... 省略 if BConfig.Listen.EnableHTTP { go func() { app.Server.Addr = addr logs.Info("http server Running on http://%s", app.Server.Addr) // 默認配置false不強制tcp4 if BConfig.Listen.ListenTCP4 { //... // 忽略 默認false } else { // 關鍵點 ListenAndServe: app.Server的類型就是*http.Server 因此這裏就啓動了http服務 if err := app.Server.ListenAndServe(); err != nil { logs.Critical("ListenAndServe: ", err) time.Sleep(100 * time.Microsecond) endRunning <- true } } }() } // 阻塞到服務啓動 <-endRunning } // 看到這裏http已經啓動了 並且是註冊Handler的方式
接着去找這個Handler的ServeHTTP方法,經過上面的這段代碼app.Server.Handler = app.Handlers
,咱們找到了下面的定義,Handler便是ControllerRegister
的值,因此每次親求來了就會去執行ControllerRegister.ServeHTTP(rw http.ResponseWriter, r *http.Request)
。測試
// src/github.com/astaxie/beego/app.go func init() { // 調用 建立beego框架實例的方法 BeeApp = NewApp() } // App結構體 type App struct { // 關鍵的請求回調Handler Handlers *ControllerRegister // http包的服務 Server *http.Server } func NewApp() *App { // 初始化http handler cr := NewControllerRegister() // 建立beego 實例 app := &App{Handlers: cr, Server: &http.Server{}} return app }
經過咱們追beego.Run()
的代碼,目前咱們獲得的結論就是:
http.HandleFun()
的定義路由策略,而是註冊Handler的方式因此beego就是經過beego.Router()
本身管理路由,若是http請求來了,回調ControllerRegister.ServeHTTP(rw http.ResponseWriter, r *http.Request)
方法,在ControllerRegister.ServeHTTP(rw http.ResponseWriter, r *http.Request)
方法中去匹配路由並執行對應的controller 也就是beegoControllerInterface
類型的控制器的方法,好比RESTFUL或者自定義啊等。
首先路由文件是如何加載的,咱們發如今main.go
文件裏導入了路由包:
package main import ( // 導入routers包 只執行init方法 _ "beego-code-read/routers" "github.com/astaxie/beego" ) func main() { beego.Run() }
上面咱們啓動了http服務,接着關鍵就是beego.Router()
如何註冊路由了。追了下代碼以下:
beego.Router() -> BeeApp.Handlers.Add(rootpath, c, mappingMethods...) -> ControllerRegister.addWithMethodParams(pattern, c, nil, mappingMethods...) -> ControllerRegister.addToRouter(method, pattern string, r *ControllerInfo) -> *Tree.AddRouter(pattern string, runObject interface{})
最後就是在*Tree.AddRouter()
完成了路由註冊,這裏的代碼邏輯暫時就先不看了,至此這個beego框架的流程就其本理順了,最後咱們在回頭總結下整個流程以下圖:
備註:go導入包至關於入棧過程,先import後執行init