func fl() { println ("fl") } func f2 () { println ("f2") } funcs := make(map[string] func ()) funcs ["fl"] = fl funcs ["f2"] = fl funcs ["fl"]() funcs ["f2"]()但是这有个缺陷,就是 map 的 Value 类型被写成 func(),不同参数和返回值的类型的函数并不能通用。将 map 的 Value 定义为 interface{} 空接口类型即可以解决该问题,但需要借助类型断言或反射来实现,通过类型断言实现等于又绕回去了,反射是一种可行的办法。
package main import ( "fmt" "github.com/codegangsta/inject" ) type S1 interface{} type S2 interface{} func Format(name string, company S1, level S2, age int) { fmt.Printf("name = %s, company=%s, level=%s, age = %d!\n", name, company, level, age) } func main() { //控制实例的创建 inj := inject.New() //实参注入 inj.Map("tom") inj.MapTo("tencent", (*S1)(nil)) inj.MapTo("T4", (*S2)(nil)) inj.Map(23) //函数反转调用 inj.Invoke(Format) }运行结果如下:
name = tom, company=tencent, level=T4, age = 23!
可见 inject 提供了一种注入参数调用函数的通用功能,inject.New() 相当于创建了一个控制实例,由其来实现对函数的注入调用。inject 包不但提供了对函数的注入,还实现了对 struct 类型的注入,示例代码如下所示:package main import ( "fmt" "github.com/codegangsta/inject" ) type S1 interface{} type S2 interface{} type Staff struct { Name string `inject` Company S1 `inject` Level S2 `inject` Age int `inject` } func main() { //创建被注入实例 s := Staff{} //控制实例的创建 inj := inject.New() //初始化注入值 inj.Map("tom") inj.MapTo("tencent", (*S1)(nil)) inj.MapTo("T4", (*S2)(nil)) inj.Map(23) //实现对 struct 注入 inj.Apply(&s) //打印结果 fmt.Printf("s = %v\n", s) }运行结果如下:
s = {tom tencent T4 23}
可以看到 inject 提供了一种对结构类型的通用注入方法。至此,我们仅仅从宏观层面了解 iniect 能做什么,下面从源码实现角度来分析 inject。type Injector interface { Applicator Invoker TypeMapper SetParent(Injector) } type Applicator interface { Apply(interface{}) error } type Invoker interface { Invoke(interface{}) ([]reflect.Value, error) } type TypeMapper interface { Map(interface{}) TypeMapper MapTo(interface{}, interface{}) TypeMapper Get(reflect.Type) reflect.Value }Injector 接口是 Applicator、Invoker、TypeMapper 接口的父接口,所以实现了 Injector 接口的类型,也必然实现了 Applicator、Invoker 和 TypeMapper 接口:
type injector struct { values map[reflect.Type]reflect.Value parent Injector } 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 New() Injector { return &injector{ values: make(map[reflect.Type]reflect.Value), } }injector 是 inject 包中唯一定义的 struct,所有的操作都是基于 injector struct 来进行的,它有两个成员 values 和 parent。values 用于保存注入的参数,是一个用 reflect.Type 当键、reflect.Value 为值的 map,理解这点将有助于理解 Map 和 MapTo。
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
func (i *injector) Map(val interface{}) TypeMapper { i.values[reflect.TypeOf(val)] = reflect.ValueOf(val) return i } func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper { i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val) return i } func (i *injector) Get(t reflect.Type) reflect.Value { val := i.values[t] if !val.IsValid() && i.parent != nil { val = i.parent.Get(t) } return val } func (i *injector) SetParent(parent Injector) { i.parent = parent }Map 和 MapTo 方法都用于注入参数,保存于 injector 的成员 values 中。这两个方法的功能完全相同,唯一的区别就是 Map 方法用参数值本身的类型当键,而 MapTo 方法有一个额外的参数可以指定特定的类型当键。但是 MapTo 方法的第二个参数 ifacePtr 必须是接口指针类型,因为最终 ifacePtr 会作为 InterfaceOf 方法的参数。
package main import ( "fmt" "reflect" "github.com/codegangsta/inject" ) type SpecialString interface{} func main() { inj := inject.New() inj.Map("C语言中文网") inj.MapTo("Golang", (*SpecialString)(nil)) inj.Map(20) fmt.Println("字符串是否有效?", inj.Get(reflect.TypeOf("Go语言入门教程")).IsValid()) fmt.Println("特殊字符串是否有效?", inj.Get(inject.InterfaceOf((*SpecialString)(nil))).IsValid()) fmt.Println("int 是否有效?", inj.Get(reflect.TypeOf(18)).IsValid()) fmt.Println("[]byte 是否有效?", inj.Get(reflect.TypeOf([]byte("Golang"))).IsValid()) inj2 := inject.New() inj2.Map([]byte("test")) inj.SetParent(inj2) fmt.Println("[]byte 是否有效?", inj.Get(reflect.TypeOf([]byte("Golang"))).IsValid()) }运行结果如下所示:
字符串是否有效? true
特殊字符串是否有效? true
int 是否有效? true
[]byte 是否有效? false
[]byte 是否有效? true
func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) { t := reflect.TypeOf(f) var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func for i := 0; i < t.NumIn(); i++ { argType := t.In(i) val := inj.Get(argType) if !val.IsValid() { return nil, fmt.Errorf("Value not found for type %v", argType) } in[i] = val } return reflect.ValueOf(f).Call(in), nil }Invoke 方法用于动态执行函数,当然执行前可以通过 Map 或 MapTo 来注入参数,因为通过 Invoke 执行的函数会取出已注入的参数,然后通过 reflect 包中的 Call 方法来调用。Invoke 接收的参数 f 是一个接口类型,但是 f 的底层类型必须为 func,否则会 panic。
package main import ( "fmt" "github.com/codegangsta/inject" ) type SpecialString interface{} func Say(name string, gender SpecialString, age int) { fmt.Printf("My name is %s, gender is %s, age is %d!\n", name, gender, age) } func main() { inj := inject.New() inj.Map("张三") inj.MapTo("男", (*SpecialString)(nil)) inj2 := inject.New() inj2.Map(25) inj.SetParent(inj2) inj.Invoke(Say) }运行结果如下:
My name is 张三, gender is 男, age is 25!
上面的例子如果没有定义 SpecialString 接口作为 gender 参数的类型,而把 name 和 gender 都定义为 string 类型,那么 gender 会覆盖 name 的值。func (inj *injector) Apply(val interface{}) error { v := reflect.ValueOf(val) for v.Kind() == reflect.Ptr { v = v.Elem() } if v.Kind() != reflect.Struct { return nil } t := v.Type() for i := 0; i < v.NumField(); i++ { f := v.Field(i) structField := t.Field(i) if f.CanSet() && structField.Tag == "inject" { ft := f.Type() v := inj.Get(ft) if !v.IsValid() { return fmt.Errorf("Value not found for type %v", ft) } f.Set(v) } } return nil }Apply 方法是用于对 struct 的字段进行注入,参数为指向底层类型为结构体的指针。可注入的前提是:字段必须是导出的(也即字段名以大写字母开头),并且此字段的 tag 设置为
`inject`
。package main import ( "fmt" "github.com/codegangsta/inject" ) type SpecialString interface{} type TestStruct struct { Name string `inject` Nick []byte Gender SpecialString `inject` uid int `inject` Age int `inject` } func main() { s := TestStruct{} inj := inject.New() inj.Map("张三") inj.MapTo("男", (*SpecialString)(nil)) inj2 := inject.New() inj2.Map(26) inj.SetParent(inj2) inj.Apply(&s) fmt.Println("s.Name =", s.Name) fmt.Println("s.Gender =", s.Gender) fmt.Println("s.Age =", s.Age) }运行结果如下:
s.Name = 张三
s.Gender = 男
s.Age = 26
本文链接:http://task.lmcjl.com/news/5679.html