您的位置:首页 > 编程语言 > Go语言

martini框架源码阅读分析

2015-09-22 11:29 549 查看
Martini核心部分Injector

Injector模块总体构造



injector对象:

type injector struct {
values map[reflect.Type]reflect.Value  // 保存<类型,值>对
parent Injector
}


TypeMaper接口

type TypeMapper interface {
Map(interface{}) TypeMapper // 类型映射
MapTo(interface{}, interface{}) TypeMapper //将值映射为指定的类型
Set(reflect.Type, reflect.Value) TypeMapper // 设置类型值
Get(reflect.Type) reflect.Value
}


Injectorde主体是一个Injecotr接口,Injector接口组合了Application,Invoker,以及TypeMaper接口。核心函数Invoker,通过传入一个interface,进行后续的处理。interface的具体类型必须为函数,否则会panic。通过reflect进行反射获取interface的具体函数类型。

func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {
t := reflect.TypeOf(f)
 
var in = make([]reflect.Value, t.NumIn()) //如果传入的interface不是函数,则panic
for i := 0; i < t.NumIn(); i++ {
argType := t.In(i)
val := inj.Get(argType) // 通过函数参数类型获取函数值,injector注册器通过map保存<类型,值>对
1b023
。
if !val.IsValid() {
return nil, fmt.Errorf("Value not found for type %v", argType)
}
 
in[i] = val
}
 
return reflect.ValueOf(f).Call(in), nil
}


injector使用例子:

package main
 
import (
"fmt"
"github.com/codegangsta/inject"
)
 
type myString interface{}
 
func test1(st string, myst myString) {
fmt.Println(st, myst)
}
 
var (
st1 = "hello"
st2 = "WORLD"
    myst myString =="haha"
)
 
func main() {
 
inj := inject.New() \\生成injector实例
inj.Map(st1)  \\ st1注册为默认的类型
inj.MapTo(st2, (*myString)(nil))\\ st2注册为指定的myStrig类型
inj.Invoke(test1)
\\ 函数test1通过参数了类型获取参数具体的值
    fmt.Println(inject.InterfaceOf(&myst))  // myst必须为指向interface的指针,否则panic。
}
}
输出:

martini服务模块

Martini结构:






使用Matrini框架时,可以自己直接使用框架内置的classicMartini。使用方法:

package main
 
import (
"github.com/go-martini/martini"
"fmt"
"reflect"
)
 
func test1(st string) string {
return st
}
func test2(num int) int {
fmt.Println(num)
return num
}
 
type INT interface{}
 
func test3(num INT, st string) string {
st = st + fmt.Sprint(reflect.TypeOf(num))
return st
}
func main() {
m := martini.Classic()  //生成martini实例
m.Get("/hello", test2, test1)// 注册路由
m.Get("/hi", test3)
m.Map("a") // 注册函数参数值
m.Map(1)
m.MapTo("HI", (*INT)(nil))
m.Run() //启动监听服务
}


工作流程:







1.使用Classic生成martini实例:

func Classic() *ClassicMartini {
r := NewRouter()
m := New() //生成martini实例
m.Use(Logger()) //注册Handler,实际是往martini.handlers里append一个Handler
m.Use(Recovery())
m.Use(Static("public"))
m.MapTo(r, (*Routes)(nil))
m.Action(r.Handle) // 注册路由处理函数,m.action=r.Handler
return &ClassicMartini{m, r}
}


2.注册路由函数

路由器实现:



Router接口:

type Router interface {
Routes
 
// Group adds a group where related routes can be added.
Group(string, func(Router), ...Handler)
// Get adds a route for a HTTP GET request to the specified matching pattern.
Get(string, ...Handler) Route
// Patch adds a route for a HTTP PATCH request to the specified matching pattern.
Patch(string, ...Handler) Route
// Post adds a route for a HTTP POST request to the specified matching pattern.
Post(string, ...Handler) Route
// Put adds a route for a HTTP PUT request to the specified matching pattern.
Put(string, ...Handler) Route
// Delete adds a route for a HTTP DELETE request to the specified matching pattern.
Delete(string, ...Handler) Route
// Options adds a route for a HTTP OPTIONS request to the specified matching pattern.
Options(string, ...Handler) Route
// Head adds a route for a HTTP HEAD request to the specified matching pattern.
Head(string, ...Handler) Route
// Any adds a route for any HTTP method request to the specified matching pattern.
Any(string, ...Handler) Route
// AddRoute adds a route for a given HTTP method request to the specified matching pattern.
AddRoute(string, string, ...Handler) Route
 
// NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
NotFound(...Handler)
 
// Handle is the entry point for routing. This is used as a martini.Handler
Handle(http.ResponseWriter, *http.Request, Context)
}


例子中使用Get注册路由,Martini中注册路由与其他框架不同的地方在于,路由的函数可以是任意类型,而且同一个路劲可以注册多个处理函数。

注册路由时,实际内部都是使用router.addroute()

func (r *router) addRoute(method string, pattern string, handlers []Handler) *route {
if len(r.groups) > 0 {
groupPattern := ""
h := make([]Handler, 0)
for _, g := range r.groups {
groupPattern += g.pattern
h = append(h, g.handlers...)
}
 
pattern = groupPattern + pattern
h = append(h, handlers...) //将注册的方法append进handlers ,请求路劲到此路由的时候会遍历调用该handlers,直到函数有返回值
handlers = h
}
fmt.Println("handlers:", reflect.ValueOf(handlers))
route := newRoute(method, pattern, handlers) // 生成指定路径方法的路由
fmt.Println("route:", reflect.ValueOf(route.handlers))
route.Validate()
r.appendRoute(route)
return route
}


addRoute生成一个route并添加进router.routes。当客户端请求时,遍历改routes获取对应路由。

addRoute方法调用了newRoute返回一个*route

route结构

type route struct {
method   string
regex    *regexp.Regexp
handlers []Handler
pattern  string
name     string
}


newRoute将注册的方法添加进handlers。

例子中的m.Get("/hello",
test2,
test1)及将test1,test2这两个函数添加去route.handlers.

当请求路径为/hello时,就会调用这两个方法。

3.map为函数参数注入参数值

4.run启动服务器监听请求

func (m *Martini) ServeHTTP(res http.ResponseWriter, req *http.Request) {
//res.Write([]byte("hello"))
 
m.createContext(res, req).run()
}


ServerHTTP是每一个request请求实例的入口。

对于每一个req实例,createContext生成一个上下文。

func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context {
c := &context{inject.New(), m.handlers, m.action, NewResponseWriter(res), 0} //生成context实例
c.SetParent(m)
c.MapTo(c, (*Context)(nil)) //注入请求参数值
c.MapTo(c.rw, (*http.ResponseWriter)(nil))
c.Map(req)
return c
}


run进行路由处理。

func (c *context) run() {
 
for c.index <= len(c.handlers) { // 遍历handlers,handlers实际为例子中使用USE注册的logger,recovery。当左右相等时,则调用m.action,及路由处理器
 
_, err := c.Invoke(c.handler())
if err != nil {
panic(err)
}
c.index += 1 // 计数器,用来遍历注册的路由器
 
if c.Written() {
return
}
}
}


run首先调用第一个注册的Logger。

func Logger() Handler {
return func(res http.ResponseWriter, req *http.Request, c Context, log *log.Logger) {
start := time.Now()
 
addr := req.Header.Get("X-Real-IP")
if addr == "" {
addr = req.Header.Get("X-Forwarded-For")
if addr == "" {
addr = req.RemoteAddr
}
}
 
log.Printf("Started %s %s for %s", req.Method, req.URL.Path, addr)
 
rw := res.(ResponseWriter)
c.Next() //调用第二个注册的recovery
 
log.Printf("Completed %v %s in %v\n", rw.Status(), http.StatusText(rw.Status()), time.Since(start))
}
}


Martini一个神奇的地方在于Next这个函数,log,recovery记忆Handle通过next实现嵌套调用。

其次调用Recovery

func Recovery() Handler {
return func(c Context, log *log.Logger) {
defer func() { // 捕获路由处理panic
if err := recover(); err != nil {
stack := stack(3)
log.Printf("PANIC: %s\n%s", err, stack)
 
// Lookup the current responsewriter
val := c.Get(inject.InterfaceOf((*http.ResponseWriter)(nil)))
res := val.Interface().(http.ResponseWriter)
 
// respond with panic message while in development mode
var body []byte
if Env == Dev {
res.Header().Set("Content-Type", "text/html")
body = []byte(fmt.Sprintf(panicHtml, err, err, stack))
} else {
body = []byte("500 Internal Server Error")
}
 
res.WriteHeader(http.StatusInternalServerError)
if nil != body {
res.Write(body)
}
}
}()
 
c.Next() // 调用Handle进行请求路由处理
}
}


router.Handle路由处理

 
func (r *router) Handle(res http.ResponseWriter, req *http.Request, context Context) {
fmt.Println(reflect.TypeOf(r.getRoutes()))
bestMatch := NoMatch
var bestVals map[string]string
var bestRoute *route
for _, route := range r.getRoutes() {
 
match, vals := route.Match(req.Method, req.URL.Path)
fmt.Println(match, vals)
if match.BetterThan(bestMatch) {
bestMatch = match // 遍历查找匹配的路径路由
bestVals = vals
bestRoute = route
if match == ExactMatch {
break
}
}
}
if bestMatch != NoMatch {
fmt.Println("bestvals", bestVals)
params := Params(bestVals)
context.Map(params)
bestRoute.Handle(context, res) // 路由器逻辑处理
return
}
 
// no routes exist, 404
c := &routeContext{context, 0, r.notFounds}
context.MapTo(c, (*Context)(nil))
c.run()
}


路由业务逻辑处理route.Handle

func (r *route) Handle(c Context, res http.ResponseWriter) {
context := &routeContext{c, 0, r.handlers} // 初始化context,例子中对于/hello路径的请求r.handlers =[test2,test1]
c.MapTo(context, (*Context)(nil))
c.MapTo(r, (*Route)(nil))
context.run() // 路由业务逻辑处理,遍历r.handlers获取业务逻辑方法
}


run业务逻辑处理

func (r *routeContext) run() {
for r.index < len(r.handlers) {// 遍历handlers,实际实现时可以重写这一方法,自定义要调用方法的顺序。通过设置index可以自定义调用
handler := r.handlers[r.index]
vals, err := r.Invoke(handler)
if err != nil {
panic(err)
}
r.index += 1
 
// if the handler returned something, write it to the http response 
//如果r.handlers的函数有返回值,则把返回值写入resp并设置r.writern()的返回值为true
if len(vals) > 0 {
ev := r.Get(reflect.TypeOf(ReturnHandler(nil)))
handleReturn := ev.Interface().(ReturnHandler)
handleReturn(r, vals)
}
 
if r.Written() {
return
}
}
}


实际使用Martini框架时,也可以根据需要自定义重写martini结果。重写martini结构后续的流程处理也是类似的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  martini 框架 源码 go