Golang攔截器的一種實現(xiàn)

前言

說起攔截器,大家一定會想起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)代理的類圖很簡單,如下所示:

proxy.png

我們在產(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)對其入口出口消息的攔截。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • //Clojure入門教程: Clojure – Functional Programming for the J...
    葡萄喃喃囈語閱讀 4,021評論 0 7
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,733評論 25 709
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • 草莽灘,榛荒岸,夜泊江渚,星隱燈殘。 秋水連,秋聲斷,霜染銀絲秋風(fēng)亂。 念家山菊掩柴闌,寒煙裊裊,月華皎皎,誰倚臥難歡。
    沙子666閱讀 176評論 1 2
  • 所謂的成功,就是每天進步一點點;最簡單的事情,往往最難做到。 學(xué)習(xí)定投策略:定期等額購買某一支或幾只成長型股票。對...
    hhzha0閱讀 118評論 0 0

友情鏈接更多精彩內(nèi)容