go利用(*interface{})(nil)傳遞參數(shù)類型

原創(chuàng)文章轉(zhuǎn)載請(qǐng)注明出處

最近閱讀Martini的源碼踊餐,讀到了inject這部分景醇,injectMartini的代碼中無(wú)處不在,是Martini框架的絕對(duì)核心吝岭。

先看看injector類型的聲明:

type injector struct {       
  values map[reflect.Type]reflect.Value       
  parent Injector
}

撇開(kāi) parent不看三痰,values是一個(gè)映射表,用于保存注入的參數(shù)窜管,它是一個(gè)用reflect.Type當(dāng)鍵散劫、reflect.Value為值的map。

parent Injector又是什么鬼幕帆?

// Injector represents an interface for mapping and injecting dependencies into structs
// and function arguments.
type Injector interface {
    Applicator
    Invoker
    TypeMapper
    // SetParent sets the parent of the injector. If the injector cannot find a
    // dependency in its Type map it will check its parent before returning an
    // error.
    SetParent(Injector)
}

// Applicator represents an interface for mapping dependencies to a struct.
type Applicator interface {
    // Maps dependencies in the Type map to each field in the struct
    // that is tagged with 'inject'. Returns an error if the injection
    // fails.
    Apply(interface{}) error
}

// Invoker represents an interface for calling functions via reflection.
type Invoker interface {
    // Invoke attempts to call the interface{} provided as a function,
    // providing dependencies for function arguments based on Type. Returns
    // a slice of reflect.Value representing the returned values of the function.
    // Returns an error if the injection fails.
    Invoke(interface{}) ([]reflect.Value, error)
}

// TypeMapper represents an interface for mapping interface{} values based on type.
type TypeMapper interface {
    // Maps the interface{} value based on its immediate type from reflect.TypeOf.
    Map(interface{}) TypeMapper
    // Maps the interface{} value based on the pointer of an Interface provided.
    // This is really only useful for mapping a value as an interface, as interfaces
    // cannot at this time be referenced directly without a pointer.
    MapTo(interface{}, interface{}) TypeMapper
    // Provides a possibility to directly insert a mapping based on type and value.
    // This makes it possible to directly map type arguments not possible to instantiate
    // with reflect like unidirectional channels.
    Set(reflect.Type, reflect.Value) TypeMapper
    // Returns the Value that is mapped to the current type. Returns a zeroed Value if
    // the Type has not been mapped.
    Get(reflect.Type) reflect.Value
}

Injector是注入接口聲明的組合获搏,我們先關(guān)注TypeMapper這個(gè)接口,從源碼可以得知Map和MapTo是用來(lái)映射數(shù)據(jù)類型和數(shù)據(jù)到values map[reflect.Type]reflect.Value的方法失乾。

Map方法相對(duì)來(lái)說(shuō)比較簡(jiǎn)單常熙,利用反射獲取對(duì)象的type。

func (i *injector) Map(val interface{}) TypeMapper {
    i.values[reflect.TypeOf(val)] = reflect.ValueOf(val)
    return i
}

現(xiàn)在我們先假設(shè)參數(shù)中有多個(gè)string時(shí)碱茁,values map[reflect.Type]reflect.Value這個(gè)map只會(huì)保存最后一個(gè)string的映射裸卫,那我們?cè)撊绾翁幚聿拍芡暾谋4嫠械膕tring參數(shù)呢?

考慮interface類型在底層的實(shí)現(xiàn)(type,data)纽竣,inject庫(kù)實(shí)現(xiàn)了一個(gè)從interface指針中獲取類型的函數(shù)InterfaceOf墓贿,而MapTo則利用InterfaceOf來(lái)獲取傳入的數(shù)據(jù)類型。

func InterfaceOf(value interface{}) reflect.Type {       
  t := reflect.TypeOf(value)       

  for t.Kind() == reflect.Ptr {              
    t = t.Elem()       
  }       

  if t.Kind() != reflect.Interface {              
    panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)")       
  }       
  return t
}

func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper {
    i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val)
    return i
}

簡(jiǎn)直是神來(lái)之筆,再找個(gè)別人的例子:

package main

import ( 
  "fmt" 
  "github.com/codegangsta/inject"
)

type SpecialString interface{}

func main() {   
  fmt.Println(inject.InterfaceOf((*interface{})(nil)))      
  fmt.Println(inject.InterfaceOf((*SpecialString)(nil)))
}

輸出

interface {}
main.SpecialString

看到了嗎募壕?指向接口的空指針,雖然data是nil语盈,但是我們只要它的type舱馅。分步解釋一下:

//以(*SpecialString)(nil)為例
t := reflect.TypeOf(value) //t是*main.SpecialString,t.Kind()是ptr,t.Elem()是main.SpecialString
for t.Kind() == reflect.Ptr { //循環(huán)判斷刀荒,也許是指向指針的指針
  t = t.Elem() //Elem returns a type's element type.
}
if t.Kind() != reflect.Interface {
  ... //如果不是Interface類型代嗤,報(bào)panic
}
return t //返回(*SpecialString)(nil)的元素原始類型

interface{}是什么,在go里面interface{}就是萬(wàn)能的Any缠借。inject利用了(*interface{})(nil)攜帶數(shù)據(jù)類型的特點(diǎn)干毅,只用一個(gè)空指針就搞定了數(shù)據(jù)類型的傳輸,而且擴(kuò)展了同類型數(shù)據(jù)的綁定泼返。

讓我們到martini.go去看看這個(gè)注入是怎么用的吧硝逢。

// Martini represents the top level web application. inject.Injector methods can be invoked to map services on a global level.
type Martini struct {       
  inject.Injector       
  handlers []Handler       
  action   Handler       
  logger   *log.Logger
}

// New creates a bare bones Martini instance. Use this method if you want to have full control over the middleware that is used.
func New() *Martini {       
  m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(os.Stdout, "[martini] ", 0)}       
  m.Map(m.logger)       
  m.Map(defaultReturnHandler())       
  return m
}

func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context {
    c := &context{inject.New(), m.handlers, m.action, NewResponseWriter(res), 0}
    c.SetParent(m)
    c.MapTo(c, (*Context)(nil))
    c.MapTo(c.rw, (*http.ResponseWriter)(nil))
    c.Map(req)
    return c
}

自定義的Martini結(jié)構(gòu)體包含了inject.Injector接口,所以可以很方便的注入logger绅喉。后續(xù)Invoke中間件的時(shí)候渠鸽,自然就可以通過(guò)InjectorGet方法獲取logger對(duì)象。context則使用了MapTo方法注入了Contexthttp.ResponseWriter這兩個(gè)接口類型柴罐。

那么Invoke的時(shí)候又是如何調(diào)用函數(shù)并且注入?yún)?shù)的呢徽缚?請(qǐng)移步《Invoke如何動(dòng)態(tài)傳參》

我是咕咕雞,一個(gè)還在不停學(xué)習(xí)的全棧工程師革屠。
熱愛(ài)生活凿试,喜歡跑步,家庭是我不斷向前進(jìn)步的動(dòng)力似芝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末那婉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子国觉,更是在濱河造成了極大的恐慌吧恃,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件麻诀,死亡現(xiàn)場(chǎng)離奇詭異痕寓,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蝇闭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門呻率,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人呻引,你說(shuō)我怎么就攤上這事礼仗。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵元践,是天一觀的道長(zhǎng)韭脊。 經(jīng)常有香客問(wèn)我,道長(zhǎng)单旁,這世上最難降的妖魔是什么沪羔? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮象浑,結(jié)果婚禮上蔫饰,老公的妹妹穿的比我還像新娘。我一直安慰自己愉豺,他們只是感情好篓吁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著蚪拦,像睡著了一般杖剪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上外盯,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天摘盆,我揣著相機(jī)與錄音,去河邊找鬼饱苟。 笑死孩擂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的箱熬。 我是一名探鬼主播类垦,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼城须!你這毒婦竟也來(lái)了蚤认?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤糕伐,失蹤者是張志新(化名)和其女友劉穎砰琢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體良瞧,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡陪汽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了褥蚯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挚冤。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖赞庶,靈堂內(nèi)的尸體忽然破棺而出训挡,到底是詐尸還是另有隱情澳骤,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布澜薄,位于F島的核電站为肮,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏肤京。R本人自食惡果不足惜弥锄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蟆沫。 院中可真熱鬧,春花似錦温治、人聲如沸饭庞。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)舟山。三九已至,卻和暖如春卤恳,著一層夾襖步出監(jiān)牢的瞬間累盗,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工突琳, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留若债,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓拆融,卻偏偏與公主長(zhǎng)得像蠢琳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子镜豹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 此文為本人學(xué)習(xí)guice的過(guò)程中傲须,翻譯的官方文檔,如有不對(duì)的地方趟脂,歡迎指出泰讽。另外還有一些附件說(shuō)明、吐槽昔期、疑問(wèn)點(diǎn)已卸,持...
    李眼鏡閱讀 3,495評(píng)論 2 5
  • "Unterminated string literal.": "未終止的字符串文本。", "Identifier...
    兩個(gè)心閱讀 8,371評(píng)論 0 4
  • 原創(chuàng)文章轉(zhuǎn)載請(qǐng)注明出處 前言 Martini框架是使用Go語(yǔ)言作為開(kāi)發(fā)語(yǔ)言的一個(gè)強(qiáng)力的快速構(gòu)建模塊化web應(yīng)用與服...
    咕咕鷄閱讀 996評(píng)論 0 2
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理镇眷,服務(wù)發(fā)現(xiàn)咬最,斷路器,智...
    卡卡羅2017閱讀 134,662評(píng)論 18 139
  • Spark SQL, DataFrames and Datasets Guide Overview SQL Dat...
    草里有只羊閱讀 18,326評(píng)論 0 85