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

相关文章

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

Python 迭代器和生成器概念及场景分析

《Python迭代器和生成器概念及场景分析》yield是Python中实现惰性计算和协程的核心工具,结合send()、throw()、close()等方法,能够构建高效、灵活的数据流和控制流模型,这... 目录迭代器的介绍自定义迭代器省略的迭代器生产器的介绍yield的普通用法yield的高级用法yidle

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3

Python Dash框架在数据可视化仪表板中的应用与实践记录

《PythonDash框架在数据可视化仪表板中的应用与实践记录》Python的PlotlyDash库提供了一种简便且强大的方式来构建和展示互动式数据仪表板,本篇文章将深入探讨如何使用Dash设计一... 目录python Dash框架在数据可视化仪表板中的应用与实践1. 什么是Plotly Dash?1.1