golang martini 源碼閱讀筆記之martini核心

繼上一篇關於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
		}
	}
}

 如理解有錯誤,歡迎在評論指出,不勝感激!數組

相關文章
相關標籤/搜索