繼上一篇關於inject注入的筆記,理解了martini的關鍵核心之一:依賴注入。注入回調函數,由運行時進行主動調用執行。這一篇主要是註解martini的骨架martini.go的實現,下面先從一個簡單的martini使用實例,即建立一個最簡單的http服務器開始。git
server.gogithub
//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.goweb
package martini import ( "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 logger func (m *Martini) Logger(logger *log.Logger) { m.logger = logger m.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()) // 添加日誌處理中間件 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 // 其實就是最後一個handler action 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 += 1 c.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 對初六進行回調,返回結構存儲在context if err != nil { panic(err) } c.index += 1 // for循環先執行調用處理,再更新索引,所以與 Next中更新索引不衝突 if c.Written() { return } } }
如理解有錯誤,歡迎在評論指出,不勝感激!數組