前言
这个源码分析没什么意义,仅个人感兴趣进行探究而已
提出问题
今天看别人写的gin代码的时候,发现中间件始终都是写在响应函数的上面
func main() { r := gin.Default() r.Use(wxf666) r.GET("/", func(c *gin.Context) { fmt.Println("Now in callback func") c.JSON(200, "首页") }) r.Run(":8080") }
那能不能写在下面呢?
func main() { r := gin.Default() r.GET("/", func(c *gin.Context) { fmt.Println("Now in callback func") c.JSON(200, "首页") }) r.Use(wxf666) r.Run(":8080") }
先说结论,能写下面,但是不生效,即调用链不会调用wxf666这个函数
分析源码
初探----首先观察r.Get函数
步入
r.GET("/", func(c *gin.Context))
步入
group.handle(http.MethodGet, relativePath, handlers)
步入,这里通过名称观察到handlers = group.combineHandlers(handlers)
,可能是添加调用链的地方,我们进去看看
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes { absolutePath := group.calculateAbsolutePath(relativePath) handlers = group.combineHandlers(handlers) group.engine.addRoute(httpMethod, absolutePath, handlers) return group.returnObj() }
注意到这里返roup.Handlers是对外暴露的函数切片,这里将对外暴露的函数切片和传入的响应函数组合并返回,那么我们回到上层,看看给谁了
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain { finalSize := len(group.Handlers) + len(handlers) if finalSize >= int(abortIndex) { panic("too many handlers") } mergedHandlers := make(HandlersChain, finalSize) copy(mergedHandlers, group.Handlers) copy(mergedHandlers[len(group.Handlers):], handlers) return mergedHandlers }
发现新的调用链传递给了这一句,步入看看
group.engine.addRoute(httpMethod, absolutePath, handlers)
相关的就是这一句,可以才想到这里就是将路径与调用链做了一个映射
root.addRoute(path, handlers)
再探----再次观察r.Use函数
步入
r.Use(wxf666)
步入第一句看看里面实现
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes { engine.RouterGroup.Use(middleware...) engine.rebuild404Handlers() engine.rebuild405Handlers() return engine }
发现这里直接把中间件函数添加到对外暴露的函数切片的尾部了
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes { group.Handlers = append(group.Handlers, middleware...) return group.returnObj() }
梳理流程
r.Get
mergedHandlers := make(HandlersChain, finalSize) copy(mergedHandlers, group.Handlers) copy(mergedHandlers[len(group.Handlers):], handlers)
r.Use
group.Handlers = append(group.Handlers, middleware...)
其实到这里就已经能看出来了
问题总结
如果r.Use写在r.Get前面,那么中间件函数就会在对外暴露的切片中了,一旦r.Get时,就会将copy(mergedHandlers, group.Handlers)
,此时中间件函数就被添加入调用链了
而如果r.Use写在r.Get后面,那么copy(mergedHandlers, group.Handlers)
的时候,中间件函数并不在对外暴露的函数切片中,所以没有被添加进调用链,最终只被添加进对外暴露的切片,始终没有被添加进调用链