Go-Web框架中AOP方案的實(shí)現(xiàn)方式
寫在前面
最近不是在跟兔兔的七天系列嘛,目前是跟到了Web框架(好吧,這才是剛開始)。關(guān)于Web框架這一塊,兔兔的文章是跟完了,也做完了,現(xiàn)在是在做總結(jié)??偨Y(jié)到AOP方案的時(shí)候,就有點(diǎn)蒙圈了,所以打算寫下這篇文章記錄總結(jié)兩種AOP方案。
Gin的AOP實(shí)現(xiàn)方案
其實(shí)兔兔的AOP方案和Gin的AOP方案很相似。都是通過下標(biāo)控制一個(gè)中間件進(jìn)入到下一個(gè)中間件的。當(dāng)下標(biāo)大于中間件的個(gè)數(shù)的時(shí)候,整個(gè)中間件就會(huì)往回執(zhí)行
特點(diǎn)
- 中間件是是現(xiàn)在上下文中的
- 通過下標(biāo)進(jìn)入下一個(gè)中間件
具體看下Gin的實(shí)現(xiàn)源碼
具體看下兔兔的實(shí)現(xiàn)源碼
其實(shí)是很類似的。對(duì)于這個(gè)方法,理解起來是沒什么難度的。下面我就講講這個(gè)AOP方案的實(shí)現(xiàn)流程吧。
中間件注冊(cè)是通過Use
方法,這個(gè)方法是RouterGroup
路由組提供的,在Gin中實(shí)際是抽象出了一個(gè)接口
2. 被注冊(cè)的中間件是保存在路由組的屬性中,上圖中圈住的部分
3. 在ServerHTTP
方法中匹配命中的路由視圖函數(shù)、符合條件的中間件。
4. 將命中的視圖函數(shù)添加到中間件列表中
5. 執(zhí)行Next
方法。每當(dāng)中間件函數(shù)中有Next
方法,就會(huì)再一次進(jìn)入到Next
方法,由于選取要執(zhí)行的中間件是通過c.index
控制的,每次進(jìn)來都會(huì)自加1。當(dāng)所有的中間件都執(zhí)行完了,index的值就超過了中間件的個(gè)數(shù)。也就是退出了遞歸。遞歸最底層的方法都執(zhí)行完成了,就開始一層一層返回了。
責(zé)任鏈制的AOP實(shí)現(xiàn)原理
對(duì)于責(zé)任鏈制的AOP方案,原理和洋蔥模式是一樣,(順帶提一下,Gin的設(shè)計(jì)模式叫做洋蔥模式)。它的任務(wù)是一直構(gòu)建"視圖函數(shù)",最終構(gòu)建成這樣的形式
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...") }
但是上述這種形式太不優(yōu)雅了,我們就使用一個(gè)責(zé)任鏈的設(shè)計(jì)模式來實(shí)現(xiàn)、優(yōu)化,但是精髓就是構(gòu)建出這種樣子。
注意
- 我們需要對(duì)中間件進(jìn)行一個(gè)包裝,就是說對(duì)中間件的函數(shù)簽名進(jìn)行一個(gè)包裝
- 我們的視圖函數(shù)不用變
- 可以理解的是中間件函數(shù)就是生成一個(gè)視圖函數(shù),只不過生成的視圖函數(shù)嵌入了一些別的通用邏輯
// 視圖函數(shù)簽名 type HandleFunc func(ctx *Context) // 中間件函數(shù)簽名 type Middleware func(next HandleFunc) HandeFunc
下文我統(tǒng)一將中間函數(shù)和命中的視圖函數(shù)叫做中間件。不過命中的視圖函數(shù)會(huì)加上特殊二字
中間件函數(shù)簽名解釋一下:
- 參數(shù)
next
是下一次需要的中間件邏輯 - 返回值是一個(gè)特殊的中間件,這個(gè)就是當(dāng)前這個(gè)中間件的邏輯
具體的一個(gè)中間件示例代碼
func Logger() Middleware { return func(next HandleFunc) HandleFunc { return func(ctx *Context){ fmt.Println("請(qǐng)求來了") next(ctx) fmt.Println("請(qǐng)求走了") } } }
解釋示例代碼:
Logger
函數(shù)返回一個(gè)Middleware
函數(shù)。返回一個(gè)視圖函數(shù)構(gòu)造器fmt.Println("請(qǐng)求來了")
和fmt.Println("請(qǐng)求走了")
兩行代碼分別嵌在next
下一個(gè)需要執(zhí)行的中間件邏輯
上述已經(jīng)講完了關(guān)于責(zé)任鏈制的AOP方法的具體原理。但是到這里還不夠,這只是原理,還沒有和我們的框架進(jìn)行適配。接下來就具體講講怎么和咱們的框架進(jìn)行適配。
責(zé)任鏈制的AOP方案應(yīng)用
首先還是需要定義好視圖函數(shù)、中間件函數(shù)的簽名
// 視圖函數(shù)簽名 type HandleFunc func(ctx *Context) // 中間件函數(shù)簽名 type Middleware func(next HandleFunc) HandeFunc
我們的AOP是集成在server層面上的,在Gin中,AOP是集成在Context上下文層面上面的。其實(shí)這個(gè)沒有多大的區(qū)別的,無(wú)非是設(shè)計(jì)者的設(shè)計(jì)思想而已。
說我們的AOP是集成在server層面其實(shí)還不太準(zhǔn)確,準(zhǔn)確來說是在路由組RouterGroup
上的。
路由組RouterGroup
定義一個(gè)屬性保存當(dāng)前組的所有中間件
2. 路由組RouterGroup
提供一個(gè)方法Use
注冊(cè)中間件
3. 匹配路由的時(shí)候需要找出當(dāng)前路由所屬哪個(gè)組,并將其所有的中間件抽離出來
4. 組裝中間件
在組裝中間價(jià)的時(shí)候,我們需要注意:
- 我們注冊(cè)的中間件是有順序的
- 我們執(zhí)行的中間件也是要有順序的
- 先注冊(cè)先執(zhí)行前半部分
基于上述的注意事項(xiàng),我們組裝的中間件應(yīng)該是從后往前組裝的。這里需要花點(diǎn)時(shí)間想想
我們可以這樣想,如果是按照正向的順序遍歷middlewares
的話,最后的handler
應(yīng)該是最后一個(gè)注冊(cè)的中間件才對(duì)。這和我們的期待是完全相反的,從這也就能解釋為什么我們需要從后往前組裝中間件了。只有這樣,最后handler
才是我們第一個(gè)注冊(cè)的中間件方法。
最后就執(zhí)行handler
方法即可。
總結(jié)
- 基于洋蔥模型的AOP方案和基于責(zé)任鏈制的AOP方案本質(zhì)沒有區(qū)別
- Gin的AOP是集成在上下文中,我們的是集成在
RouterGroup
上。兩種方式?jīng)]有區(qū)別
到此這篇關(guān)于Go-Web框架中AOP方案的實(shí)現(xiàn)方式的文章就介紹到這了,更多相關(guān)Go AOP實(shí)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用dep 配置golang 開發(fā)環(huán)境的操作方法
下面小編就為大家?guī)硪黄褂胐ep 配置golang 開發(fā)環(huán)境的操作方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09Go 實(shí)現(xiàn)英尺和米的簡(jiǎn)單單位換算方式
這篇文章主要介紹了Go 實(shí)現(xiàn)英尺和米的簡(jiǎn)單單位換算方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-04-04從零封裝Gin框架實(shí)現(xiàn)日志初始化及切割歸檔功能
這篇文章主要為大家介紹了從零封裝Gin框架實(shí)現(xiàn)日志初始化及切割歸檔功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01Go泛型實(shí)戰(zhàn)教程之如何在結(jié)構(gòu)體中使用泛型
這篇文章主要介紹了Go泛型實(shí)戰(zhàn)教程之如何在結(jié)構(gòu)體中使用泛型,根據(jù)Go泛型使用的三步曲提到的:類型參數(shù)化、定義類型約束、類型實(shí)例化我們一步步來定義我們的緩存結(jié)構(gòu)體,需要的朋友可以參考下2022-07-07Go語(yǔ)言學(xué)習(xí)之Switch語(yǔ)句的使用
這篇文章主要通過一些示例為大家介紹一下Go語(yǔ)言中Switch語(yǔ)句的基本語(yǔ)法以及使用,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-06-06go panic時(shí)如何讓函數(shù)返回?cái)?shù)據(jù)?
今天小編就為大家分享一篇關(guān)于go panic時(shí)如何讓函數(shù)返回?cái)?shù)據(jù)?,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-04-04