原創(chuàng)文章轉(zhuǎn)載請(qǐng)注明出處
最近閱讀Martini
的源碼踊餐,讀到了inject
這部分景醇,inject
在Martini
的代碼中無(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ò)Injector
的Get
方法獲取logger
對(duì)象。context
則使用了MapTo方法注入了Context
和http.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)力似芝。