這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
繼上一篇關於inject注入的筆記,理解了martini的關鍵核心之一:依賴注入。注入回呼函數,由運行時進行主動調用執行。這一篇主要是註解martini的骨架martini.go的實現,下面先從一個簡單的martini使用執行個體,即建立一個最簡單的http伺服器開始。
server.go
//martini使用簡單一實例 package main import "github.com/go-martini/martini" func main() { m := martini.Classic() //擷取一個martini執行個體 m.Get("/", func() string { // 使用者自訂路由規則 return "Hello world!" }) m.Run() // 運行伺服器 }
martini.go
package martiniimport ("log""net/http""os""reflect""github.com/codegangsta/inject")// Martini represents the top level web application. inject.Injector methods can be invoked to map services on a global level.type Martini struct {inject.Injector // 注入工具,利用反射實現函數回調handlers []Handler // 儲存處理http請求的所有中介軟體action Handler // 路由匹配以及路由處理,在所有中介軟體都處理完之後執行logger *log.Logger // 日誌工具}// 基礎骨架:具備基本的注入與反射調用功能// New creates a bare bones Martini instance. Use this method if you want to have full control over the middleware that is used.func New() *Martini {m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(os.Stdout, "[martini] ", 0)}m.Map(m.logger)m.Map(defaultReturnHandler())return m}// Handlers sets the entire middleware stack with the given Handlers. This will clear any current middleware handlers.// Will panic if any of the handlers is not a callable function// 設定所有的中介軟體func (m *Martini) Handlers(handlers ...Handler) {m.handlers = make([]Handler, 0)for _, handler := range handlers {m.Use(handler)}}// Action sets the handler that will be called after all the middleware has been invoked. This is set to martini.Router in a martini.Classic().// 設定真正的路由處理器,所有中介軟體執行完之後才會執行func (m *Martini) Action(handler Handler) {validateHandler(handler)m.action = handler}// Logger sets the loggerfunc (m *Martini) Logger(logger *log.Logger) {m.logger = loggerm.Map(m.logger)}// Use adds a middleware Handler to the stack. Will panic if the handler is not a callable func. Middleware Handlers are invoked in the order that they are added.// 添加一個中介軟體處理器,每一個http請求都會先執行, 按照添加的順序依次執行func (m *Martini) Use(handler Handler) {validateHandler(handler)m.handlers = append(m.handlers, handler)}// http介面,每一次http請求 使用者層級處理的入口// ServeHTTP is the HTTP Entry point for a Martini instance. Useful if you want to control your own HTTP server.func (m *Martini) ServeHTTP(res http.ResponseWriter, req *http.Request) {println("call....")m.createContext(res, req).run() // 每一個請求建立一個上下文,儲存一些必要的資訊,之後開始處理請求}// Run the http server on a given host and port.// http 伺服器啟動func (m *Martini) RunOnAddr(addr string) {// TODO: Should probably be implemented using a new instance of http.Server in place of// calling http.ListenAndServer directly, so that it could be stored in the martini struct for later use.// This would also allow to improve testing when a custom host and port are passed.logger := m.Injector.Get(reflect.TypeOf(m.logger)).Interface().(*log.Logger)logger.Printf("listening on %s (%s)\n", addr, Env)logger.Fatalln(http.ListenAndServe(addr, m)) // m是整個架構控制的開始}// Run the http server. Listening on os.GetEnv("PORT") or 3000 by default.func (m *Martini) Run() {port := os.Getenv("PORT")if len(port) == 0 {port = "3000"}host := os.Getenv("HOST")m.RunOnAddr(host + ":" + port)}//建立請求上下文,與大部分的web架構一樣,使用內容相關的方式儲存處理請求過程中的相關資料func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context {// NewResponseWriter 對res進行了封裝修飾,添加了一些其他功能,比如過濾器之類的c := &context{inject.New(), m.handlers, m.action, NewResponseWriter(res), 0}c.SetParent(m)c.MapTo(c, (*Context)(nil)) //Context 為介面類型, c 是實現了Context介面的具體類型結構體//這裡就是做一種介面與實現的映射, 使用具體實現的時候只能使用映射類型已經實現的介面c.MapTo(c.rw, (*http.ResponseWriter)(nil)) // rw 同樣是映射到interface{}類型c.Map(req) // req 是一種具體類型,這裡只是想儲存這個值return c}//經典的搭配,整合了路由以及martini核心功能// ClassicMartini represents a Martini with some reasonable defaults. Embeds the router functions for convenience.type ClassicMartini struct {*Martini //Router // 匿名變數類型,需要一個實現了所有的介面的對象,這樣ClassicMartini執行個體可以無縫調用Router的介面,比如m.Get(pattern, handler)}// Classic creates a classic Martini with some basic default middleware - martini.Logger, martini.Recovery and martini.Static.// Classic also maps martini.Routes as a service.func Classic() *ClassicMartini {r := NewRouter() // 基礎路由器,用於儲存使用者自訂路由規則以及處理器m := New() // martini基礎架構m.Use(Logger()) // 添加Tlog中介軟體m.Use(Recovery()) // 伺服器異常錯誤時進行處理m.Use(Static("public")) // 靜態檔案處理 m.MapTo(r, (*Routes)(nil)) // nil 表示這裡只需要一個類型m.Action(r.Handle) //所有router中介軟體執行完才執行的處理,相當於是正式的路由匹配處理,中介軟體做一些web常規必要的處理return &ClassicMartini{m, r}}// Handler can be any callable function. Martini attempts to inject services into the handler's argument list.// Martini will panic if an argument could not be fullfilled via dependency injection.// 定義Handler類型為一個泛型type Handler interface{}//檢查Handler是否為函數類型func validateHandler(handler Handler) {if reflect.TypeOf(handler).Kind() != reflect.Func {panic("martini handler must be a callable func")}}// Context represents a request context. Services can be mapped on the request level from this interface.type Context interface {// 包含了另一個介面類型的所有介面,Context的執行個體必須實現所有的介面,或者包含一個匿名的具體案例實現該所有介面inject.Injector // 中介軟體的順序執行過程中按順序執行時,使用Next介面不斷的更新索引指向下一個中介軟體Next() // Written returns whether or not the response for this context has been written.// 返回是否 http請求已經處理完並發送應答的標識Written() bool }// http請求處理的上下文執行個體type context struct {// 包含一個介面類型,初始化的時候需要一個具體實現的執行個體, 一個依賴注入的案例,注入具體函數,運行時按順序回調各個函數inject.Injector // handler數組,處理http請求時按順序一個一個執行handlers []Handler // 其實就是最後一個handleraction Handler // 對http.ResponseWriter的進一步封裝,加入更多功能,比如過濾器,Before After等處理rw ResponseWriter // 表示當前第n個hanlder的索引index int }// 取出當前第n個處理器,如果索引值到達最大值,則返回action函數,即開始路由匹配邏輯func (c *context) handler() Handler {if c.index < len(c.handlers) {return c.handlers[c.index]}if c.index == len(c.handlers) {return c.action}panic("invalid index for context handler")}// 更新指向下一個處理器,之後繼續執行剩餘處理器對請求的處理func (c *context) Next() {c.index += 1c.run()}// 判斷是否已發送應答,若已發送,則不需要再進行處理func (c *context) Written() bool {return c.rw.Written()}// 擷取當前的處理器,並處理http請求,執行一連串的處理func (c *context) run() {for c.index <= len(c.handlers) {_, err := c.Invoke(c.handler()) // Invoke 對初六進行回調,返回結構儲存在contextif err != nil {panic(err)}c.index += 1 // for迴圈先執行調用處理,再更新索引,因此與 Next中更新索引不衝突if c.Written() {return}}}
如理解有錯誤,歡迎在評論指出,不勝感激!