martini框架源码阅读分析

2024-03-13 21:18

本文主要是介绍martini框架源码阅读分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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保存<类型,值>对。
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框架源码阅读分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/806230

相关文章

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

解决1093 - You can‘t specify target table报错问题及原因分析

《解决1093-Youcan‘tspecifytargettable报错问题及原因分析》MySQL1093错误因UPDATE/DELETE语句的FROM子句直接引用目标表或嵌套子查询导致,... 目录报js错原因分析具体原因解决办法方法一:使用临时表方法二:使用JOIN方法三:使用EXISTS示例总结报错原

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

Spring 框架之Springfox使用详解

《Spring框架之Springfox使用详解》Springfox是Spring框架的API文档工具,集成Swagger规范,自动生成文档并支持多语言/版本,模块化设计便于扩展,但存在版本兼容性、性... 目录核心功能工作原理模块化设计使用示例注意事项优缺点优点缺点总结适用场景建议总结Springfox 是

python中Hash使用场景分析

《python中Hash使用场景分析》Python的hash()函数用于获取对象哈希值,常用于字典和集合,不可变类型可哈希,可变类型不可,常见算法包括除法、乘法、平方取中和随机数哈希,各有优缺点,需根... 目录python中的 Hash除法哈希算法乘法哈希算法平方取中法随机数哈希算法小结在Python中,

Java Stream的distinct去重原理分析

《JavaStream的distinct去重原理分析》Javastream中的distinct方法用于去除流中的重复元素,它返回一个包含过滤后唯一元素的新流,该方法会根据元素的hashcode和eq... 目录一、distinct 的基础用法与核心特性二、distinct 的底层实现原理1. 顺序流中的去重

Python的端到端测试框架SeleniumBase使用解读

《Python的端到端测试框架SeleniumBase使用解读》:本文主要介绍Python的端到端测试框架SeleniumBase使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全... 目录SeleniumBase详细介绍及用法指南什么是 SeleniumBase?SeleniumBase