Martini的初始化和运行

原创文章转载请注明出处

前言

Martini框架是使用Go语言作为开发语言的一个强力的快速构建模块化web应用与服务的开发框架。

阅读本文之前可以先去看看《go利用(*interface{})(nil)传递参数类型》和《Invoke如何动态传参》,这两篇文章介绍了Martini深度依赖的inject包的一些实现细节。

Martini的官方文档中提到Martini完全兼容http.HandlerFunc接口.,底下谈到martini.Context的初始化就会有说明。

先来看看Martini的结构体。

// 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
    action   Handler
    logger   *log.Logger
}

inject.Injectorinject接口实例,Martini高度依赖inject
handlers是切片存储Hander类型,Handler是自定义类型。

type Handler interface{}

Martini有两种处理器,中间件处理器和请求处理器。中间件处理器通过Use方法将中间件追加保存到handlers切片中,请求处理器需要搭配路由进行存储。

初始化

为了更快速的启用Martini, martini.Classic() 提供了一些默认的方便Web开发的工具:

  m := martini.Classic()
  // ... middleware and routing goes here
  m.Run()

下面是Martini核心已经包含的功能 martini.Classic():

  • Request/Response Logging (请求/响应日志) - martini.Logger
  • Panic Recovery (容错) - martini.Recovery
  • Static File serving (静态文件服务) - martini.Static
  • Routing (路由) - martini.Router

下面的这些服务已经被包含在核心Martini中: martini.Classic():

  • *log.Logger - Martini的全局日志.
  • martini.Context - http request context (请求上下文).
  • martini.Params - map[string]string of named params found by route matching. (名字和参数键值对的参数列表)
  • martini.Routes - Route helper service. (路由协助处理)
  • http.ResponseWriter - http Response writer interface. (响应结果的流接口)
  • *http.Request - http Request. (http请求)

martini.Context是每次请求的上下文。

// Context represents a request context. Services can be mapped on the request level from this interface.
type Context interface {
    inject.Injector
    // Next is an optional function that Middleware Handlers can call to yield the until after
    // the other Handlers have been executed. This works really well for any operations that must
    // happen after an http request
    Next()
    // Written returns whether or not the response for this context has been written.
    Written() bool
}

它是什么时候被创建的呢?还记得《理解go的function types》吗?

type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request)

martini.go实现了ServerHTTP方法。

// 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) {
    m.createContext(res, req).run()
}

因为Martini实现了http.HandlerFunc接口,所以它可以很简单的应用到现有Go服务器的子集中。

package hello

import (
  "net/http"
  "github.com/go-martini/martini"
)

func init() {
  m := martini.Classic()
  m.Get("/", func() string {
    return "Hello world!"
  })
  http.Handle("/", m)
}

martini.Context的实例m.context就是在go服务器初始化的时候通过ServeHTTP被创建的。

我们已经知道Martini如何应用到Go服务器的子集,那么当服务器运行的时候,处理器是如何被执行的呢?

运行

我们看看context结构体

type context struct {
    inject.Injector
    handlers []Handler
    action   Handler
    rw       ResponseWriter
    index    int
}

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()
}

func (c *context) run() {
    for c.index <= len(c.handlers) {
        _, err := c.Invoke(c.handler())
        if err != nil {
            panic(err)
        }
        c.index += 1

        if c.Written() {
            return
        }
    }
}

context实现了Context接口,自然也组合了inject.Injector。处理器处理请求的时候通过run()循环遍历调用handlers中的所有中间件和路由处理方法。

看到这里是不是发现一个很眼熟的函数Invoke(),没错又是injectInvoke()《Invoke如何动态传参》,我之前的文章分析过injectMartini的核心。

我们可以总结一下处理器的运行流程,初始化的时候使用inject接口绑定数据,在上下文中保存处理器函数。服务器运行以后,每次处理请求就在上下文中执行处理器函数,并且利用依赖注入动态传参完成请求处理。

后记

本文没有涉及到Martini的路由设计和中间件的使用技巧,改日再专门写一篇文章进行介绍。

我是咕咕鸡,一个还在不停学习的全栈工程师。
热爱生活,喜欢跑步,家庭是我不断向前进步的动力。

你可能感兴趣的:(Martini的初始化和运行)