Gin HandlersChain 调用解析
前言
Gin 可以说是Golang WEB应用比较广泛框架,在博主使用时一直对其内部中间件和Group handle的调用比较好奇,这次仅从FuncHandle的调用做一次解析。
一、HandlersChain 是什么?
在gin的context.go文件中存在下述定义
type Context struct {
// ... 忽略上面
handlers HandlersChain
index int8
// ... 忽略下面
}
// HandlersChain defines a HandlerFunc array.
type HandlersChain []HandlerFunc
// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)
通过代码可以,HandlersChain
实质就是一个方法数组,每个方法的方法签名为 func(*Context)。
这个方法数组记录了形如:gin.Use(func..), gin.Get("", func), gin.Group("", func)
中出现的func,并在gin初始化的时候按顺序,将这些func放到同一个数组
,等待被依次调用。
二、调用方式
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
可以看出来Next内部维护了一个for循环来依次调用方法链中的方法。并用一个全局的index来维护context方法调用指针,防止出现每调用一次Next就全部调用一次所有方法。
context的Next就是方法链的调用入口。源码的调用如下
// gin.go
func (engine *Engine) handleHTTPRequest(c *Context) {
// ... 忽略上列代码
if value.handlers != nil {
c.handlers = value.handlers
c.fullPath = value.fullPath
c.Next()
c.writermem.WriteHeaderNow()
return
}
// ... 忽略下列代码
}
三、源码模拟
package main
func main() {
// 初始化
context := NewContext()
for i := 0; i < 5; i++ {
temp := i
context.add(func(ctx *Context) {
// 注释next后 4 3 2 1 0
// 不注释为 0 1 2 3 4
//ctx.Next()
print(temp)
})
}
// 入口
context.Next()
}
func NewContext() *Context {
return &Context{
funcIndex: -1,
FuncHandles: make([]FuncHandle, 0),
}
}
type Context struct {
funcIndex int
FuncHandles []FuncHandle
}
type FuncHandle func(ctx *Context)
// 调用
func (this *Context) Next() {
this.funcIndex++
for ; this.funcIndex < len(this.FuncHandles); this.funcIndex++ {
this.FuncHandles[this.funcIndex](this)
}
}
// 添加Handle
func (this *Context) add(handle FuncHandle) {
this.FuncHandles = append(this.FuncHandles, handle)
}
四、总结
通过上述代码我们可以总结出,Next只是一个方法的调用入口。
如果func中不写c.Next()
,会由顶层调用(最开始调用Next()的方法
)进行for循环遍历;如果func写了c.Next()
就想到与是一个链式的调用。