likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 52

写在前面

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

Gin的AOP实现方案

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

特点

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

具体看下Gin的实现源码

Go-Web框架中AOP方案的实现方式 具体看下兔兔的实现源码

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

  1. 中间件注册是通过Use方法,这个方法是RouterGroup路由组提供的,在Gin中实际是抽象出了一个接口 Go-Web框架中AOP方案的实现方式

Go-Web框架中AOP方案的实现方式 2. 被注册的中间件是保存在路由组的属性中,上图中圈住的部分 3. 在ServerHTTP方法中匹配命中的路由视图函数符合条件的中间件

Go-Web框架中AOP方案的实现方式 4. 将命中的视图函数添加到中间件列表中

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

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

责任链制的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...")
}

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

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

注意

  1. 我们需要对中间件进行一个包装,就是说对中间件的函数签名进行一个包装
  2. 我们的视图函数不用变
  3. 可以理解的是中间件函数就是生成一个视图函数,只不过生成的视图函数嵌入了一些别的通用逻辑
// 视图函数签名
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("请求走了")
       }
   }
}

Go-Web框架中AOP方案的实现方式 解释示例代码:

  1. Logger函数返回一个Middleware函数。返回一个视图函数构造器
  2. 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上的。

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

Go-Web框架中AOP方案的实现方式 2. 路由组RouterGroup提供一个方法Use注册中间件

Go-Web框架中AOP方案的实现方式 3. 匹配路由的时候需要找出当前路由所属哪个组,并将其所有的中间件抽离出来

Go-Web框架中AOP方案的实现方式 4. 组装中间件

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

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

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

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

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

  1. 最后就执行handler方法即可。

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

总结

  1. 基于洋葱模型的AOP方案和基于责任链制的AOP方案本质没有区别
  2. Gin的AOP是集成在上下文中,我们的是集成在RouterGroup上。两种方式没有区别
转载自:https://juejin.cn/post/7227139379105038392
评论
请登录