Go-Web框架中AOP方案的实现方式

 更新时间:2023年06月05日 11:35:00   作者:今天捡到一百块钱  
本文主要介绍了Go-Web框架中AOP方案的实现方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

写在前面

最近不是在跟兔兔的七天系列嘛,目前是跟到了Web框架(好吧,这才是刚开始)。关于Web框架这一块,兔兔的文章是跟完了,也做完了,现在是在做总结。总结到AOP方案的时候,就有点蒙圈了,所以打算写下这篇文章记录总结两种AOP方案。

Gin的AOP实现方案

其实兔兔的AOP方案和Gin的AOP方案很相似。都是通过下标控制一个中间件进入到下一个中间件的。当下标大于中间件的个数的时候,整个中间件就会往回执行

image.png

特点

  • 中间件是是现在上下文中的
  • 通过下标进入下一个中间件

具体看下Gin的实现源码

image.png

具体看下兔兔的实现源码

image.png

其实是很类似的。对于这个方法,理解起来是没什么难度的。下面我就讲讲这个AOP方案的实现流程吧。

中间件注册是通过Use方法,这个方法是RouterGroup路由组提供的,在Gin中实际是抽象出了一个接口

image.png

image.png

2. 被注册的中间件是保存在路由组的属性中,上图中圈住的部分

3. 在ServerHTTP方法中匹配命中的路由视图函数符合条件的中间件

image.png

4. 将命中的视图函数添加到中间件列表中

image.png

5. 执行Next方法。每当中间件函数中有Next方法,就会再一次进入到Next方法,由于选取要执行的中间件是通过c.index控制的,每次进来都会自加1。当所有的中间件都执行完了,index的值就超过了中间件的个数。也就是退出了递归。递归最底层的方法都执行完成了,就开始一层一层返回了。

image.png

责任链制的AOP实现原理

对于责任链制的AOP方案,原理和洋葱模式是一样,(顺带提一下,Gin的设计模式叫做洋葱模式)。它的任务是一直构建"视图函数",最终构建成这样的形式

func m() {
   fmt.Println("coming middleware1...")
   func() {
      fmt.Println("coming middleware2...")
      func() {
         fmt.Println("coming middleware3...")
         func() {
            fmt.Println("coming middleware4...")
            func() {
               fmt.Println("coming middleware5...")
               func() {
               }()
               fmt.Println("outing middleware5...")
            }()
            fmt.Println("outing middleware4...")
         }()
         fmt.Println("outing middleware3...")
      }()
      fmt.Println("outing middleware2...")
   }()
   fmt.Println("outing middleware1...")
}

但是上述这种形式太不优雅了,我们就使用一个责任链的设计模式来实现、优化,但是精髓就是构建出这种样子。

image.png

注意

  • 我们需要对中间件进行一个包装,就是说对中间件的函数签名进行一个包装
  • 我们的视图函数不用变
  • 可以理解的是中间件函数就是生成一个视图函数,只不过生成的视图函数嵌入了一些别的通用逻辑
// 视图函数签名
type HandleFunc func(ctx *Context)
// 中间件函数签名
type Middleware func(next HandleFunc) HandeFunc

下文我统一将中间函数和命中的视图函数叫做中间件。不过命中的视图函数会加上特殊二字

中间件函数签名解释一下:

  • 参数next是下一次需要的中间件逻辑
  • 返回值是一个特殊的中间件,这个就是当前这个中间件的逻辑

具体的一个中间件示例代码

func Logger() Middleware {
   return func(next HandleFunc) HandleFunc {
       return func(ctx *Context){
           fmt.Println("请求来了")
           next(ctx)
           fmt.Println("请求走了")
       }
   }
}

image.png

解释示例代码:

  • Logger函数返回一个Middleware函数。返回一个视图函数构造器
  • fmt.Println("请求来了")fmt.Println("请求走了")两行代码分别嵌在next下一个需要执行的中间件逻辑

上述已经讲完了关于责任链制的AOP方法的具体原理。但是到这里还不够,这只是原理,还没有和我们的框架进行适配。接下来就具体讲讲怎么和咱们的框架进行适配。

责任链制的AOP方案应用

首先还是需要定义好视图函数、中间件函数的签名

// 视图函数签名
type HandleFunc func(ctx *Context)
// 中间件函数签名
type Middleware func(next HandleFunc) HandeFunc

我们的AOP是集成在server层面上的,在Gin中,AOP是集成在Context上下文层面上面的。其实这个没有多大的区别的,无非是设计者的设计思想而已。

说我们的AOP是集成在server层面其实还不太准确,准确来说是在路由组RouterGroup上的。

路由组RouterGroup定义一个属性保存当前组的所有中间件

image.png

2. 路由组RouterGroup提供一个方法Use注册中间件

image.png

3. 匹配路由的时候需要找出当前路由所属哪个组,并将其所有的中间件抽离出来

image.png

4. 组装中间件

在组装中间价的时候,我们需要注意:

  • 我们注册的中间件是有顺序的
  • 我们执行的中间件也是要有顺序的
  • 先注册先执行前半部分

image.png

基于上述的注意事项,我们组装的中间件应该是从后往前组装的。这里需要花点时间想想

我们可以这样想,如果是按照正向的顺序遍历middlewares的话,最后的handler应该是最后一个注册的中间件才对。这和我们的期待是完全相反的,从这也就能解释为什么我们需要从后往前组装中间件了。只有这样,最后handler才是我们第一个注册的中间件方法。

最后就执行handler方法即可。

image.png

总结

  • 基于洋葱模型的AOP方案和基于责任链制的AOP方案本质没有区别
  • Gin的AOP是集成在上下文中,我们的是集成在RouterGroup上。两种方式没有区别

 到此这篇关于Go-Web框架中AOP方案的实现方式的文章就介绍到这了,更多相关Go AOP实现内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 快速掌握Go语言正/反向代理

    快速掌握Go语言正/反向代理

    这篇文章主要介绍了快速掌握Go语言正/反向代理的相关资料,需要的朋友可以参考下
    2022-11-11
  • 使用dep 配置golang 开发环境的操作方法

    使用dep 配置golang 开发环境的操作方法

    下面小编就为大家带来一篇使用dep 配置golang 开发环境的操作方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • 详解Go语言如何解析带注释的json

    详解Go语言如何解析带注释的json

    标准的json格式是不带注释,但是有时候为了方便理解json中各字段的含义,需要支持带注释的json,这篇文章主要介绍了Go语言解析带注释json的相关方法,希望对大家有所帮助
    2024-03-03
  • Go 实现英尺和米的简单单位换算方式

    Go 实现英尺和米的简单单位换算方式

    这篇文章主要介绍了Go 实现英尺和米的简单单位换算方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • 从零封装Gin框架实现日志初始化及切割归档功能

    从零封装Gin框架实现日志初始化及切割归档功能

    这篇文章主要为大家介绍了从零封装Gin框架实现日志初始化及切割归档功能示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • Go泛型实战教程之如何在结构体中使用泛型

    Go泛型实战教程之如何在结构体中使用泛型

    这篇文章主要介绍了Go泛型实战教程之如何在结构体中使用泛型,根据Go泛型使用的三步曲提到的:类型参数化、定义类型约束、类型实例化我们一步步来定义我们的缓存结构体,需要的朋友可以参考下
    2022-07-07
  • 使用GO操作MongoDB的方法

    使用GO操作MongoDB的方法

    这篇文章主要介绍了使用GO操作MongoDB,包括安装MongoDB驱动程序连接mongodb的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05
  • Go语言学习之Switch语句的使用

    Go语言学习之Switch语句的使用

    这篇文章主要通过一些示例为大家介绍一下Go语言中Switch语句的基本语法以及使用,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2022-06-06
  • go panic时如何让函数返回数据?

    go panic时如何让函数返回数据?

    今天小编就为大家分享一篇关于go panic时如何让函数返回数据?,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-04-04
  • Golang Slice和map的坑

    Golang Slice和map的坑

    Golang中的Slice和Map在内存空间上,有一些不同,本文主要介绍了Golang Slice和map的坑,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-04-04

最新评论