前言
說起攔截器,大家一定會想起Java語言。
Java里的攔截器是動態(tài)攔截Action調(diào)用的對象,它提供了一種機制使開發(fā)者可以定義在一個action執(zhí)行的前后執(zhí)行的代碼,也可以在一個action執(zhí)行前阻止其執(zhí)行,同時也提供了一種可以提取action中可重用部分的方式。在AOP(Aspect-Oriented Programming)中攔截器用于在某個方法或字段被訪問之前,進行攔截然后在之前或之后加入某些操作。
最近一段時間,筆者想在Golang代碼里面使用攔截器,但在github上卻沒有找到相關(guān)的庫,于是就有了自己實現(xiàn)一個攔截器的想法。
Golang沒有虛擬機,對反射的支持比Java弱很多,所以不能照搬java的實現(xiàn)方式。就在筆者決定徹底放棄使用動態(tài)代理實現(xiàn)攔截器時,突然來了靈感,并且一口氣完成了實現(xiàn)。
本文結(jié)合代碼介紹Golang攔截器的一種實現(xiàn),希望能給現(xiàn)在或?qū)硐胗脭r截器的Gopher一些思路。
產(chǎn)品代碼
我們簡單模擬一下產(chǎn)品代碼:
- 有一個Account接口,聲明了方法Query和Update,分別用于查詢帳戶和更新帳戶
- 類AccountImpl實現(xiàn)了Account接口
- 有一個簡單工廠New,用于創(chuàng)建一個Account對象
代碼如下所示:
package account
import (
"fmt"
)
type Account interface {
Query(id string) int
Update(id string, value int)
}
type AccountImpl struct {
Id string
Name string
Value int
}
func (a *AccountImpl) Query(_ string) int {
fmt.Println("AccountImpl.Query")
return 100
}
func (a *AccountImpl) Update(_ string, _ int) {
fmt.Println("AccountImpl.Update")
}
var New = func(id, name string, value int) Account {
return &AccountImpl{id, name, value}
}
我們寫一個main函數(shù):
package main
import (
"interceptor/account"
)
func main() {
id := "100111"
a := account.New(id, "ZhangSan", 100)
a.Query(id)
a.Update(id, 500)
}
運行程序:
$ go run main.go
AccountImpl.Query
AccountImpl.Update
靜態(tài)代理
靜態(tài)代理的類圖很簡單,如下所示:

我們在產(chǎn)品代碼之外實現(xiàn)一下Proxy,如下所示:
package proxy
import (
"interceptor/account"
"fmt"
)
type Proxy struct {
Account account.Account
}
func (p *Proxy) Query(id string) int {
fmt.Println("Proxy.Query begin")
value := p.Account.Query(id)
fmt.Println("Proxy.Query end")
return value
}
func (p *Proxy) Update(id string, value int) {
fmt.Println("Proxy.Update begin")
p.Account.Update(id, value)
fmt.Println("Proxy.Update end")
}
Account對象跳轉(zhuǎn)到Proxy
若要將Account對象從AccountImpl跳轉(zhuǎn)到Proxy,則需要在程序運行時重定義簡單工廠函數(shù)變量account.New。
我們在main包的init函數(shù)中重定義account.New:
package main
import (
"interceptor/account"
"interceptor/proxy"
)
func main() {
id := "100111"
a := account.New(id, "ZhangSan", 100)
a.Query(id)
a.Update(id, 500)
}
func init() {
account.New = func(id, name string, value int) account.Account {
a := &account.AccountImpl{id, name, value}
p := &proxy.Proxy{a}
return p
}
}
運行程序:
$ go run main.go
Proxy.Query begin
AccountImpl.Query
Proxy.Query end
Proxy.Update begin
AccountImpl.Update
Proxy.Update end
OK!完全符合期望,而且攔截器對產(chǎn)品代碼基本無侵入,僅需在main包的init函數(shù)中重定義簡單工廠函數(shù)變量。
小結(jié)
本文結(jié)合代碼給出了Golang攔截器的一種實現(xiàn),即“靜態(tài)代理模式 + 簡單工廠函數(shù)變量”。讀者掌握攔截器的實現(xiàn)技巧后,可以基本無侵入的實現(xiàn)微服務(wù)的trace功能。
說明:對于不滿足本方法約束的框架,比如Beego,一般需要借助框架自身的filter功能實現(xiàn)對其入口出口消息的攔截。