这两天在接触Gin,对它的动态路由功能比较感兴趣,特意做了笔记,顺便跟SpringMVC作下对比。

1.简介

 Gin是使用Go/golang语言实现的HTTP Web框架。接口简洁,性能极高。截止1.4.0版本,包含测试代码,仅14K,其中测试代码9K左右,也就是说框架源码仅5K左右。SpringMVC不用过多介绍,Java市场的一把手。

 Gin支持动态路由,简单示例如下:

import (    "github.com/gin-gonic/gin"    "net/http")func main() {    r := gin.Default()    r.GET("/hello/:name", func(context *gin.Context) {        context.String(http.StatusOK, "Hello : "+context.Param("name"))    })    r.Run()}

对比SpringMVC的例子为:

import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HelloWorldController {    @RequestMapping("/hello/{name}")    public String antUser (@PathVariable("name") String name) {        return "Hello : " + name;    }}

二者有相似的地方。

2.Gin

 Gin使用Trie树来实现动态路由。Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。

 Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。它有3个基本性质:

  1. 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
  2. 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
  3. 每个节点的所有子节点包含的字符都不相同。

如上图示,为一个保存了8个键的trie结构,"A", "to", "tea", "ted", "ten", "i", "in", "inn"。其中,键标注在节点中,值标注在节点之下。每一个完整的英文单词对应一个特定的整数。Trie可以看作是一个确定有限状态自动机,尽管边上的符号一般是隐含在分支的顺序中的。

 Trie数的常见应用场景包括:

  1. 字符串检索
  2. 词频统计
  3. 前缀检索
  4. 前缀词频统计
  5. 对所有的字符串按照字典序排序

 Gin的路由实现也是跟上面的例子类似,具体实现在tree.go文件里,主要包括trie树的构建和查找过程。

2.1. 建树过程

 先看node的定义

type node struct {    path      string           // 当前节点相对路径,即公共前缀    indices   string           // 所有孩子节点的path[0]组成的字符串,如果子节点有通配符,则为空    children  []*node          // 孩子节点    handlers  HandlersChain    // 当前节点的处理函数(包括中间件)    priority  uint32           // 当前节点及子孙节点的实际路由数量    nType     nodeType         // 节点类型    maxParams uint8            // 子孙节点的最大参数数量    wildChild bool             // 孩子节点是否有通配符(wildcard)    fullPath  string           // 完整的请求路径,各中间节点也有}

建树过程主要由两个方法来完成

// 根据给定的路径增加一个节点,主要用于处理公共前缀的分割func (n *node) addRoute(path string, handlers HandlersChain) {}// 主要用于处理新节点的插入func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) {}

流程如下:

使用addRoute方法从根节点添加一个新的路径P,如果树为空,则作为新节点直接插入,此时该节点为树中的第一个节点(根节点),path和fullPath值都为P。如果根节点存在子节点,则找到P跟根节点path的公共前缀,如果不存在公共前缀,则将新节点作为根节点的子节点加入,使用insertChild方法。如果存在公共前缀,则分裂当前节点,将根节点(当前节点)公共前缀后的内容独立为一个子节点,并挂到当前节点下,更新indices;接着获取P去掉公共前缀的第一个字符,判断当前节点indices列表是否存在相同的字符,即判断剩下的内容是要作为新节点加入,还是要继续分裂,如果需要继续分裂,则重复addRoute方法。

 以下面这段代码为例,

r.GET("/q/query", func(context *gin.Context) {    context.String(http.StatusOK,"Hello "+context.Query("name"))})r.GET("/q/qaz/:name", func(context *gin.Context) {    context.String(http.StatusOK,"Hello "+context.Query("name"))})r.GET("/q/qaj/:name/l", func(context *gin.Context) {    context.String(http.StatusOK,"Hello "+context.Query("name"))})

构建的trie树为:

2.png

这里需要指出的是,对于通配符,:xxxx或者*,会作为一个单独的节点出现。

2.2. 查找过程

查找过程比较简单,直接从根节点往下找,知道找到匹配的节点,过程如下:

3.SpringMVC

 如下为一个简单的示例:

@RestControllerpublic class HelloWorldController {    @RequestMapping("/hello/{name}")    public String antUser (@PathVariable("name") String name) {        return "hello : " + name;    }}

 之前在介绍《SpringMVC加载流程》是说过,Spring MVC路由的加载是由RequestMappingHandlerMapping来处理的。该类会查找所有符合条件的Method上的注解,然后添加到父类AbstractHandlerMethodMapping的MappingRegistry中封装为HandlerMethod进行缓存,直接以HashMap的方式。除了RequestMappingHandlerMapping外还有其他HandlerMapping,如SimpleUrlHandlerMapping,BeanNameUrlHandlerMapping等。

 当查找符合的HandlerMethod时会遍历所有的HandlerMapping,如果某HandlerMapping能够处理,返回对应的HandlerExecutionChain,同时退出循环,由HandlerAdapter来执行具体的Method,Apdater会完成入参的注入。而RequestMappingHandler的动态路由则体现在HandlerMethod的查找上,该功能主要由AbstractHandlerMethodMapping的lookupHandlerMethod方法来实现。lookupHandlerMethod方法会遍历MappingRegistry中的所有注册对象,通过PatternsRequestCondition的getMatchingCondition来判断,具体交由AntPathMatcher来实现。

 AntPathMatcher主要用来做类URLs字符串匹配,可以匹配的规则如下:

  • ?匹配一个字符
  • *匹配0个或多个字符
  • **匹配0个或多个目录

具体实现在doMatch方法中。

即SpringMVC是通过遍历所有注册的Url,对每个Url应用AntPathMatcher来判断当前请求的Url是否符合注册的通配符写法,从而找到对应的处理函数。

如下为简单的示意图:

4.参考

https://zh.wikipedia.org/wiki/Trie

easyui tree动态加载_动态路由:Gin vs SpringMVC相关推荐

  1. go语言 不支持动态加载_动态语言支持

    go语言 不支持动态加载 本文是我们名为" 高级Java "的学院课程的一部分. 本课程旨在帮助您最有效地使用Java. 它讨论了高级主题,包括对象创建,并发,序列化,反射等. 它 ...

  2. java做类似于qq空间动态加载_实现类似微博、QQ空间等的动态加载

    微博.QQ空间等的动态加载方式属于滚屏加载技术,获取当前滚动条位置来触发onscroll()函数,向服务器发起请求,将请求得到的新的数据动态加载在页面上 本文利用该原理实现了动态加载,但不是检测当前滚 ...

  3. php动态加载js,动态加载script文件的两种方法_javascript技巧

    动态加载script到页面大约有俩方法 第一种就是利用ajax方式,把script文件代码从后台加载到前台,然后对加载到的内容通过eval()执行代码.第二种是,动态创建一个script标签,设置其s ...

  4. 动态加载___import__动态加载技术

    __import__ 可以实现模块的动态加载,Python中很多框架都使用了这种能力,如Flask的插件系统.APScheduler定时任务框架等. 这里简单看一下APScheduler定时任务框架是 ...

  5. java动态加载js_动态加载JavaSript

    在许多情况下,需要使用动态插入脚本的方式 1. 跨域访问:不同域下的脚本不能够通过该AJAX方式直接获得 2.延迟加载或预加载:由于浏览器会等所有外链脚本加载完才开始解析.为了提高用户响应速度和用户体 ...

  6. dll侧加载_动态载入DLL所需要的三个函数详解(LoadLibrary,GetProcAddress,FreeLibrary)...

    动态载入 DLL 动态载入方式是指在编译之前并不知道将会调用哪些 DLL 函数, 完全是在运行过程中根据需要决定应调用哪些函数. 方法是:用 LoadLibrary 函数加载动态链接库到内存,用 Ge ...

  7. gif 动态加载_搞笑GIF:这个游戏最大的成功就是失败!

    ҉҉҈҉҉҉҈҉҉҉҈҈҉҉҈喜҉҉҈҉҉҉҈҉҉҉҈҈҉҉҈欢҉҉҈҉҉҉҈҉҉҉҈҈҉҉҈看҉҉҈҉҉҉҈҉҉҉҈҈҉҉҈搞笑GIF动态图 ҉҉҈҉҉҉҈҉҉҉҈҈҉҉҈可҉҉҈҉҉҉҈҉҉҉҈҈ ...

  8. 使用EasyUI Tree异步加载JSON数据 生成树

    这几天因为工作需要,要做一个支持无限级的菜单. 我也是菜鸟一只,能想到的东西不多,所以用了Easy UI的tree组件. 不得不说,easyui确实很强大. 因为是无限级菜单,数据量可能有点大,所以考 ...

  9. easyUI tree 异步加载数据

    easyUI 的 Tree组件在大部分时候都要从数据库读取数据,本文基于Java语言来实现easyUI tree的异步加载. 先看一下实现的效果: 整个过程采用的是简单三层架构,无论采用哪种模式,只要 ...

最新文章

  1. Leetcode--845. 数组中的最长山脉
  2. 觅凤c语言教程,C语言程序设计教程 第1章.ppt
  3. 从数据库导出到EXCEL文件的sql语句
  4. update语句更新多条记录, 标记下
  5. 九章算法班L6 Graph Search
  6. python内置的字典dict中元素_Python内置字典;dict ,set
  7. 《Android 应用案例开发大全(第3版)》——第1.3节Android开发环境的搭建
  8. java init是什么
  9. 我的计算机 不显示桌面存储路劲,win10系统保存文件路径在桌面无法显示的操作步骤...
  10. 大数据的七大核心具体价值
  11. Windows远程桌面连接全屏切换--笔记本没有break键的办法
  12. 深度解析 ORA-01555 原因及解决方法
  13. 百度地图导航的接入(包含三种选择方式驾车、公交、步行)
  14. android微信第三方登陆混淆,Android 第三方应用接入微信平台研究情况分享(二)
  15. STM32学习之I2C协议(读写EEPROM)
  16. BUUCTF RE WP31-32 [WUSTCTF2020]level1、[GWCTF 2019]xxor
  17. 修改wowpc变色龙
  18. android单机听歌软件,听歌用什么软件好?2018听歌软件推荐
  19. 普通美国人把钱花哪儿了
  20. Java 关键字:extends, implements

热门文章

  1. mysql_unbuffered_query的_用mysql_unbuffered_query函数取大数据
  2. Java 格式化时间
  3. php 图片不让下载,php简单实现文件或图片强制下载的方法
  4. python3.x和python2.x唯一区别_Python3.x和Python2.x的区别介绍
  5. oracle dg snapshot,Oracle Broker Snapshot Standby测试
  6. 计算机病毒的危害主要体现于对计算机系统的信息破坏和,2014年中央电大专科信息技术应用理论题.doc...
  7. 搜索用计算机弹奏9277的数字,计算机基础知识参考试题(含答案)
  8. 下找到vue变量_Vue:npm run serve 到底做了什么?
  9. python花括号代替缩进_Python 为什么甩掉累赘的花括号,使用缩进来划分代码块?...
  10. js 获取当前元素的父元素的父元素的id